Chapter 1

Exercise 1-1

main.c

#include <stdio.h>

int main()
{
    printf("Hello, World\n");
}

Compilation and run:

$ gcc main.c
$ ./a.out
Hello, World
Notes:
  • In c, main is special, your program begins executing at the beginning of main. This mean that every program must have a main somewhere.

Exercise 1-2

main.c

#include <stdio.h>

int main()
{
    printf("\\t -> -\t- \n");
    printf("\\b -> -\b- \n");
    printf("\\a -> -\a- \n");
    printf("\\y -> -\y- \n");
}

Compilation and run:

$ gcc main.c
main.c: In function ‘main’:
main.c:8:32: warning: unknown escape sequence: '\y'
    8 |         printf("\\y -> -\y- \n");
      |
$ ./a.out
\t -> -     -
\b -> -
\a -> --
\y -> -y-
Notes:
  • On page 193 K&R, it is stated that if the escape sequence is not recognized the behavior is undefined. Most compiler just do a warning and ignore the \.

Exercise 1-3

main.c

#include <stdio.h>

/* print Fahrenheit-Celsius table for
* fahr= 0, 20, ..., 300 
* floating point with headings version
*/
int main()
{
    float celsius; 
    int fahr; 
    
    int start=0, end=300, step=20;

    printf("Fahrenheit  Celsius\n");
    printf("----------  -------\n");
    for (fahr = start; fahr <= end; fahr = fahr+step){
        celsius = 5.0/9.0 * (fahr-32.0);
        printf("%10d %8.1f\n", fahr, celsius);
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
Fahrenheit  Celsius
----------  -------
         0    -17.8
        20     -6.7
        40      4.4
        60     15.6
        80     26.7
       100     37.8
       120     48.9
       140     60.0
       160     71.1
       180     82.2
       200     93.3
       220    104.4
       240    115.6
       260    126.7
       280    137.8
       300    148.9

Exercise 1-4

main.c

#include <stdio.h>

/* print Celsius-Fahrenheit table for
* Ceslius= 0, 20, ..., 300 
* floating point with headings version
*/
int main()
{
    float fahr; 
    int celsius; 
    
    int start=0, end=300, step=20;

    printf("Celsius Fahrenheit\n");
    printf("------- ----------\n");
    for (celsius = start; celsius <= end; celsius = celsius+step){
        fahr = 9.0 / 5.0 * celsius + 32.0;
        printf("%7d %10.1f\n", celsius, fahr);
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
Celsius Fahrenheit
------- ----------
      0       32.0
     20       68.0
     40      104.0
     60      140.0
     80      176.0
    100      212.0
    120      248.0
    140      284.0
    160      320.0
    180      356.0
    200      392.0
    220      428.0
    240      464.0
    260      500.0
    280      536.0
    300      572.0

Exercise 1-5

main.c

#include <stdio.h>

int main()
{
    printf("Fahrenheit  Celsius\n");
    printf("----------  -------\n");

    int fahr;
    for (fahr = 300; fahr >= 0; fahr = fahr-20){
        printf("%10d %8.1f\n", fahr, 5.0/9.0 * (fahr-32.0));
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
Fahrenheit  Celsius
----------  -------
       300    148.9
       280    137.8
       260    126.7
       240    115.6
       220    104.4
       200     93.3
       180     82.2
       160     71.1
       140     60.0
       120     48.9
       100     37.8
        80     26.7
        60     15.6
        40      4.4
        20     -6.7
         0    -17.8
Notes:
  • Notice the equivalence between while and for iteration statements:

    for (expr_1 ; expr_2 ; expr_3){
        statement
    }
    

    is exactly the same as:

    expr_1;
    while(expr_2){
        statement
        expr_3;
    }
    

    (as long as continue statements are not used)

Exercise 1-6

main.c

#include <stdio.h>

int main()
{
    int res_expr;

    while (res_expr = getchar() != EOF){
        printf("%d", res_expr);
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
hello there
111111111111
Notes:
  • An assignment statement in C has the type and value of the left operand after the assignment. For example the expression

    c = getchar()
    

    evaluates to the return value of getchar() and has the same type as c. And since the evaluation is done right to left this make this kind of expressions possible:

    a = c = getchar();
    
  • This is the first time we are exposed to error handling in C. Usually functions return a special value when an error occur, for example getchar() return EOF. If we want to make a function call such as:

    c = getchar()
    

    the idiom to make the error handling is:

    if ((c = getchar()) != EOF){
        /* error handling */
    }
    
  • Because of operator precedence, the parentheses around the assignment in the previous code are necessary, always use them. Without them:

    c = getchar() != EOF;
    

    is equivalent to:

    c = (getchar() != EOF);
    

    (c is either 0 or 1)

Note

From this exercise onwards, all solutions must handle errors. You can use your system reference manuals to check what the functions you are using return on error.

Exercise 1-7

main.c

#include <stdio.h>

int main()
{
    printf("value of EOF: %d\n", EOF);
}

Compilation and run:

$ gcc main.c
$ ./a.out
value of EOF: -1
Notes:
  • We knew that EOF is an int (or can be casted to an int without problem) because it is used as the error return value of functions that return int.

  • Notice we do not check for errors with printf. In general, it is common practice to not check for errors when the the purpose of the function is to write to stdout (putchar, printf, …)

Note

It is reasonable to assume that writing to standard output will not fail. If it does fail, how would you tell the user, anyway?

Exercise 1-8

main.c

#include <stdio.h>

int main()
{
    int bc = 0;
    int c;

    while ((c = getchar()) != EOF){
        if (c == ' ' || c == '\t' || c == '\n'){
            ++bc;
        }
    }
    printf("Number of blanks, tabs and newlines: %d\n", bc);
}

Compilation and run:

$ gcc main.c
$ ./a.out
hello there
General kenobi...
Number of blanks, tabs and newlines: 4

Exercise 1-9

main.c

#include <stdio.h>

#define IN 1
#define OUT 0

int main()
{
    int blank = OUT;
    int c;

    while ((c = getchar()) != EOF) {
        if (c == ' ') {
            blank = IN;
        }else{
            if (blank == IN) {
                putchar(' ');
                blank = OUT;
            }
            putchar(c);
        }
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
This     is  a line   with   a    lot of    blanks!
This is a line with a lot of blanks!

Exercise 1-10

main.c

#include <stdio.h>

int main()
{
    int c;

    while ((c = getchar()) != EOF) {
        if (c == '\t') {
            putchar('\\');
            putchar('t');
        }else if (c == '\b') {
            putchar('\\');
            putchar('b');
        }else if (c == '\\') {
            putchar('\\');
            putchar('\\');
        }else{
            putchar(c);
        }
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
\Difficult      subjects cannot be described    with light prose
\\Difficult\tsubjects cannot be described\twith light prose

Exercise 1-11

main.c

#include <stdio.h>

#define IN 1    /* inside a word */
#define OUT 0   /* outside a word */

/* count lines, words, and characters in input */
int main()
{
    int state = OUT;
    int c, nc, nl, nw;

    nc = nl = nw = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n'){
            ++nl;
        }
        if (c != ' ' && c != '\t' && c != '\n'){
            if (state == OUT){
                ++nw;
            }
            state = IN;
        }else{
            state = OUT;
        }
    }
    printf("characters: %d\n", nc);
    printf("lines:      %d\n", nl);
    printf("words:      %d\n", nw);
}

Compilation and run:

$ gcc main.c
$ ./a.out
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
characters: 132
lines:      4
words:      20
Notes:
  • The kinds of input most likely to uncover bugs are those that test boundary conditions. -to be updated with code test practices-

Exercise 1-12

main.c

#include <stdio.h>

#define IN 1    /* inside a word */
#define OUT 0   /* outside a word */

int main()
{
    int state = OUT;
    int c;

    while ((c = getchar()) != EOF) {
        if (c != ' ' && c != '\t' && c != '\n') {
            putchar(c);
            state = IN;
        }
        else if (state == IN) {
            putchar('\n');
            state = OUT;
        }
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
Very complex system may arise from very simple rules
Very
complex
system
may
arise
from
very
simple
rules

Exercise 1-13

main.c

#include <stdio.h>

#define IN 1
#define OUT 0

#define LONGEST_WORD 45

int main()
{
    int lengths[LONGEST_WORD];
    int i;
    for (i = 0; i < LONGEST_WORD; ++i)
        lengths[i] = 0;
    int state = OUT;
    int wc=0, maxwc=0;

    int c;
    while ((c = getchar()) != EOF){
        if (c != ' ' && c != '\t' && c != '\n') {
            state = IN;
            ++wc;
        }else if (state==IN) {
            state = OUT;
            maxwc = wc > maxwc ? wc : maxwc;
            if (wc < LONGEST_WORD){
                ++lengths[wc];
            }
            wc = 0;
        }
    }
    printf("Vertical histogram\n");
    printf("------------------\n");
    int k;
    for (i = 0; i <= maxwc && i < LONGEST_WORD; ++i) {
        printf("%2d:", i);
        for (k = 0; k < lengths [i]; ++k){
            putchar('|');
        }
        putchar('\n');
    }
    printf("Horizontal Histogram\n");
    printf("--------------------\n");
    int height = 0;
    for (i = 0; i <= maxwc && i < LONGEST_WORD; ++i) {
        height = lengths[i] > height ? lengths[i] : height;
    }
    for ( ; height > 0 ; --height){
        for (i = 0; i <= maxwc && i < LONGEST_WORD; ++i) {
            if (lengths[i] >= height){
                printf(" | ");
            }else{
                printf("   ");
            }
        }
        putchar('\n');
    }
    for (i = 0; i <= maxwc && i < LONGEST_WORD; ++i) {
        printf(" %d ", i);
    }
    putchar('\n');
}

Compilation and run:

$ gcc main.c
$ ./a.out
The world is a dangerous place to live;
not because of the people who are evil,
but because of the people who don't do anything about it.
Vertical histogram
------------------
 0:
 1:|
 2:|||||
 3:|||||||||
 4:
 5:||||||
 6:||
 7:||
 8:|
 9:|
Horizontal Histogram
--------------------
          |
          |
          |
          |     |
       |  |     |
       |  |     |
       |  |     |
       |  |     |  |  |
    |  |  |     |  |  |  |  |
 0  1  2  3  4  5  6  7  8  9
Notes:
  • In C, all elements of an array has the same type and the array size never changes during its lifetime. Before C99, the array must be of constant fixed size but since C99 the array size can be an integer expression evaluated everytime the array is allocated.

Exercise 1-14

main.c

#include <stdio.h>
#include <ctype.h>

#define MAX_CHAR 256

int main()
{
    int lengths[MAX_CHAR];
    int c;
    for (c = 0; c < MAX_CHAR; ++c)
        lengths[c] = 0;

    while ((c = getchar()) != EOF){
        ++lengths[c]; /* implicit cast from signed to unsigned */
    }
    printf("Vertical Histogram\n");
    printf("------------------\n");
    for (c = 0; c < MAX_CHAR; ++c) {
        if (lengths[c] > 0 && isprint(c) && c != ' '){
            printf("%c:", c);
            while (lengths[c]-- > 0) {
                putchar('|');
            }
            putchar('\n');
        }
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
" In 1965, Gordon Moore, a founder of Intel Corporation, extrapolated from the chip technology of
the day (by which they could fabricate circuits with around 64 transistors on a single chip) to predict
that the number of transistors per chip would double every year for the next 10 years. This prediction
became known as Moore’s Law. As it turns out, his prediction was just a little bit optimistic, but also too
short-sighted. Over more than 50 years, the semiconductor industry has been able to double transistor
counts on average every 18 months."
Vertical Histogram
------------------
":||
(:|
):|
,:||||||
-:|
.:||||
0:||
1:|||
4:|
5:||
6:||
8:|
9:|
A:|
C:|
G:|
I:||
L:|
M:||
O:|
T:|
a:|||||||||||||||||||||||||||
b:||||||||||
c:|||||||||||||||||
d:|||||||||||||||
e:||||||||||||||||||||||||||||||||||||||||||
f:|||||||
g:||||
h:|||||||||||||||||||||
i:|||||||||||||||||||||||||||||
j:|
k:|
l:||||||||||||
m:|||||||
n:||||||||||||||||||||||||||
o:||||||||||||||||||||||||||||||||||||||||||||
p:||||||||||
r:|||||||||||||||||||||||||||||||||||
s:|||||||||||||||||||||||||||||
t:||||||||||||||||||||||||||||||||||||||||||||||
u:|||||||||||||||
v:||||
w:||||||
x:||
y:||||||||||
Notes:
  • The most common single-byte character encoding is ASCII. However, this code will work no matter the character encoding used, even if there were negative c values representing valid characters. We are making use of the fact that negative numbers in a two’s-complement representation map to large positive numbers in an unsigned representation.

  • We only print “printable” characters (excluding space). The function isprint from <ctype.h> determines if a character is printable. (page 249 K&R)

Exercise 1-15

main.c

#include <stdio.h>

float celsius(int fahr);

#define START 0
#define END 300
#define STEP 20

int main()
{
    int fahr;

    printf("Fahrenheit  Celsius\n");
    printf("----------  -------\n");
    for (fahr = START; fahr <= END; fahr = fahr + STEP){
        printf("%10d %8.1f\n", fahr, celsius(fahr));
    }
    return 0;
}

/* No need to check for int overflow 
*  since there is no user input and
*  the edge cases are well defined  
*/
float celsius(int fahr) 
{
    return 5.0 / 9.0 * (fahr - 32.0); 
}

Compilation and run:

$ gcc main.c
$ ./a.out
Fahrenheit  Celsius
----------  -------
         0    -17.8
        20     -6.7
        40      4.4
        60     15.6
        80     26.7
       100     37.8
       120     48.9
       140     60.0
       160     71.1
       180     82.2
       200     93.3
       220    104.4
       240    115.6
       260    126.7
       280    137.8
       300    148.9
Notes:
  • We are introduced to functions definitions. If a function is overly large or complex maybe it should be splited; a function should do just one thing and do it well.

  • Function parameters are local variables, if you modify them inside a function the original variable is not affected. They are just private, temporary copies.

Note

main is a function like any other, so it may return a a value to its caller (the environment in which the program was executed). From this exercise onwards, we will return 0 to imply normal termination and non-zero values to signal erroneous termination.

Exercise 1-16

main.c

#include <stdio.h>

#define MAXLINE 40

int mygetline(char line[], int maxline);
void copy(char to[], char from[]);

int main()
{
    char line[MAXLINE], longest[MAXLINE];
    int len=0, max=0;

    while ((len = mygetline(line, MAXLINE)) > 0){
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    }
    if (max > 0){
        printf("longest line(%d):%s", max, longest);
    }
    return 0;
}

/* 
* read a line from stdin and store it in buffer `s`
* The total line is readed but at most `n` characters 
* are stored in `s` including `\n` and the terminating '\0'.
* The total length of the line readed is returned. 
*/
int mygetline(char s[], int lim) 
{
    int c, i;

    for (i = 0; i<lim-2 &&  (c = getchar()) != EOF && c != '\n'; ++i) {
        s[i] = c;
    }
    if (c != EOF || i != 0){
        s[i] = '\n';
        ++i;
    }
    s[i] = '\0';

    while (c != EOF && c != '\n'){ /* Everylane has '\n' except EOF with 0 length */
        c = getchar();
        ++i;
    }
    return i;
}

void copy(char to[], char from[]) 
{
    int i = 0;
    for (i = 0; from[i] != '\0'; ++i){
        to[i] = from[i];
    }
    to[i] = '\0';
}

Compilation and run:

$ gcc main.c
$ ./a.out
The more you know, the more you realize you know nothing.
Imagination is more important than knowledge.  For knowledge is limited, whereas imagination embraces the entire world, stimulating progress, giving birth to evolution
The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge.
If people never did silly things, nothing intelligent would ever get done.

longest line(168):Imagination is more important than kno

Notes:

  • We have changed the name of getname to mygetname because there is a previous declaration of getline in <stdio>.

Exercise 1-17

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LINELIMIT 80

int main()
{
    long unsigned n;
    int len;
    char *s;

    s = NULL;
    n = 0;
    while ((len = getline(&s, &n, stdin)) > 0){
        if (len > LINELIMIT) {
            printf("%d:%s", len, s);
        }
    }
    free(s);
    return 0;
}

Compilation and run:

$ gcc main.c
$ ./a.out
The more you know, the more you realize you know nothing.
Imagination is more important than knowledge.  For knowledge is limited, whereas imagination embraces the entire world, stimulating progress, giving birth to evolution
168:Imagination is more important than knowledge.  For knowledge is limited, where
The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge.
83:The greatest enemy of knowledge is not ignorance, it is the illusion of knowle
If people never did silly things, nothing intelligent would ever get done.

Notes:

  • This exercise is easy after we defined the function mygetline in the previous solution (this is the purpose of well defined functions). Instead of that, we used this exercise to learn how to use getline function from stdlib. To fully understand this function we need to know pointers and dynamic memory allocation which are explained in Chapter 5 and Chapter 6 respectively. Meanwhile we are gonna use this structure:

    #include <stdlib.h>
    #include <stdlib.h>
    
    char *s;
    int len;
    long unsigned n;
    
    s = NULL;
    n = 0;
    while((len = getline(&s, &n, stdin)) > 0){
       /* ... */
    }
    free(s);
    

    knowing that len is the number of character read including \n, s is where the string is saved, and n is not used for anything. Calling free function is mandatory which is declared in <stdlib.h>.

Exercise 1-18

main.c

#include <stdio.h>
#include <stdlib.h>

int stripline(char s[]);

int main()
{
    char *s;
    long unsigned n;
    int len;

    s = NULL;
    n = 0;
    while ((len = getline(&s, &n, stdin)) > 0){
        if ((len = stripline(s)) > 0) {
            printf("%d:%s\n", len, s);
        }
    }
    free(s);
    return 0;
}

int stripline(char s[]) 
{
    int len;

    for(len = 0; s[len] != '\0'; ++len);
    --len;
    while (len >= 0 && (s[len] == ' ' || s[len] == '\t' || s[len] == '\n')) {
        s[len] = '\0';
        --len;
    }
    return len + 1;
}

Compilation and run:

$ gcc main.c
$ ./a.out
Hello
5:Hello
This has a lot of blanks at the end
35:This has a lot of blanks at the end

Notes:

  • We could have modified the function mygetline from Exercise 1-16 to achieve the functionality we wanted. However, that would make the code less modular, so instead we differentiate between get the line logic and strip the line logic in 2 different functions, each of them does one thing and does it well.

Exercise 1-19

main.c

#include <stdio.h>
#include <stdlib.h>

int stripline(char s[]);
void reverse(char s[]);

int main()
{
    char *s;
    long unsigned n;
    int len;

    s = NULL;
    n = 0;
    while ((len = getline(&s, &n, stdin)) > 0) {
        if (stripline(s) > 0){
            reverse(s);
            printf("%s\n", s);
        }
    }
    free(s);
    return 0;
}

int stripline(char s[]) 
{
    int len;

    for(len = 0; s[len] != '\0'; ++len);
    --len;
    while (len >= 0 && (s[len] == ' ' || s[len] == '\t' || s[len] == '\n')) {
        s[len] = '\0';
        --len;
    }
    return len + 1;
}

void reverse(char s[]) 
{
    int i, j;
    char c;

    for (j = 0; s[j] != '\0'; ++j);
    --j;
    for (i = 0; i < j; ++i, --j) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
Hello!
!olleH
Is this working fine??
??enif gnikrow siht sI

Notes:

  • We make use of stripline from Exercise 1-18 to get the lines without trailing blanks, tabs and newlines. This way reverse function logic is only about reversing the string.

  • Notice that we do not use an auxiliary char array to reverse the string, making it more memory efficient (at the expense maybe of some code readability).

Exercise 1-20

main.c

#include <stdio.h>
#define TABLEN 8

int main()
{
    int c;
    int pos = 0;

    while ((c = getchar()) != EOF) {
        if (c == '\t') {
            while (pos < TABLEN) {
                putchar(' ');
                ++pos;
            }
        }else{
            putchar(c);
            ++pos;
        }
        if (pos >= TABLEN || c == '\n') {
            pos = 0;
        }
    }
    return 0;
}

Compilation and run:

$ gcc main.c
$ ./a.out
This    line has        spaces and tabs intercalated    but the output will have        only spaces.
This    line has        spaces and tabs intercalated    but the output will have        only spaces.

Exercise 1-21

main.c

#include <stdio.h>

#define TABLEN 8

int main()
{
    int c;
    int nb = 0;
    int pos = 0;

    while ((c = getchar()) != EOF){
        if (c == ' '){
            ++nb;   
        }else if (c == '\t'){
            putchar(c);
            nb = 0;
        } else {
            while(nb > 0){
                putchar(' ');
                --nb;
            }
            putchar(c);
        }
        ++pos;
        if(c == '\n' || c == '\t' || pos == TABLEN){
            pos = 0;
            if (nb > 0){
                putchar('\t');
                nb = 0;
            }
        }
    }
    return 0;
}

Compilation and run:

$ gcc main.c
$ ./a.out
this      line has      lots of spaces and      tabs   but the output have      only the minimum tab    necessary.
this      line has      lots of spaces and      tabs   but the output have      only the minimum tab    necessary.

Exercise 1-22

main.c

#include <stdio.h>
#include <ctype.h>

#define MAXWORD 20
#define LINEWIDTH 40
#define TABLEN 8

int getword(char s[], int n);

int main()
{
    int c;
    int len;
    char w[MAXWORD];
    char s[LINEWIDTH];
    int pos;

    pos = 0;
    while ((c = getchar()) > 0) {
        if (c == ' '){
            putchar(c);
            ++pos;
        }else if (c == '\n'){
            putchar(c);
            pos = 0;
        }else if (c == '\t'){
            putchar(c);
            pos = (pos/TABLEN + 1) * TABLEN;
        }else{
            if(ungetc(c, stdin) == EOF){
                printf("ungetc() error\n");
                return -1;
            }
            if ((len = getword(s, MAXWORD)) == -1){
                printf("getword() error\n");
                return -1;
            }
            if (len + pos < LINEWIDTH){
                pos += len;
            }else{
                putchar('\n');
                pos = len;
            }
            printf("%s", s);
        }
    }
    return 0;
}

/* 
* A word is any sequence of characters that do not contain a blank, tab or newline. 
* At maximum `n` characters are buffered, the rest remain on stdin.
* if 0 characters are readed before encountering a  blank, tab or newline; 0 is returned.
* if 0 characters are readed before encountering EOF or there is any error; -1 is returned.
*/
int getword(char s[], int n) 
{
    int c, i;

    for (i = 0; i<n-1 && (c = getchar()) != EOF && !isblank(c) &&  c != '\n'; ++i) {
        s[i] = c;
    }
    s[i] = '\0';
    if (isblank(c) || c == '\n'){
        if (ungetc(c, stdin) == EOF){
            return -1;
        }
    }else if(c == EOF && i == 0){
        i = -1;
    }
    return i;
}

Compilation and run:

$ gcc main.c
$ ./a.out
The longest word in any of the major English language dictionaries is pneumonoultramicroscopicsilicovolcanoconiosis (45 letters), a word that refers to a lung disease contracted from the inhalation of very fine silica particles,[12] specifically from a volcano; medically, it is the same as silicosis. The word was deliberately coined to be the longest word in English, and has since been used[citation needed] in a close approximation of its originally intended meaning, lending at least some degree of validity to its claim.[6]
The longest word in any of the major
English language dictionaries is
pneumonoultramicroscopicsilicovolcanoc
oniosis (45 letters), a word that
refers to a lung disease contracted
from the inhalation of very fine silica
particles,[12] specifically from a
volcano; medically, it is the same as
silicosis. The word was deliberately
coined to be the longest word in
English, and has since been
used[citation needed] in a close
approximation of its originally
intended meaning, lending at least some
degree of validity to its claim.[6]

Exercise 1-23

main.c

#include <stdio.h>

void in_comment(void);
void in_string(char c);

/* program that eliminates C comments */
int main()
{
    int c;

    while ((c = getchar()) != EOF) {
        if (c == '/'){
            if ((c = getchar()) != '*'){
                putchar('/');
                putchar('c');
            }else{
                in_comment();
            }
        }else if (c == '"' || c == '\''){
            in_string(c);
        }else{
            putchar(c);
        }
    }
    return 0;
}

void in_comment(void)
{
    int last_c;
    int c;

    last_c = getchar();
    while ((c = getchar()) != EOF){
        if (last_c == '*' && c == '/'){
            return;
        }
        last_c = c;
    }
}

void in_string(char edge)
{
    int c;

    putchar(edge);
    while((c = getchar()) != EOF){
        putchar(c);
        if (c == '\\'){
            putchar(getchar());
        }
        if (c == edge){
            return;
        }
    }
}

Compilation and run:

$ gcc main.c
$ ./a.out
/*/* this is a ///* comment */-now it is not
-now it is not
"/*this comment is inside quotes!*/"
"/*this comment is inside quotes!*/"

Exercise 1-24

main.c

#include <stdio.h>

# define MAXNESTED 16


void in_comment(void);
void in_string(char c);
int popsep(void);
int pushsep(char c);
int check_separators(int c);


char separatorstack[MAXNESTED];
int pos = 0;


/* program that check unbalanced parentheses, brackets and braces */
int main()
{
    int c;
    int res;

    while ((c = getchar()) != EOF) {
        if (c == '/'){
            if ((c = getchar()) != '*'){
                if ((res = check_separators(c)) == -1){
                    printf("error\n");
                    return 1;
                }else if(res == 1){
                    printf("Unbalanced separators\n");
                    return 0;
                }
            }else{
                in_comment();
            }
        }else if (c == '"' || c == '\''){
            in_string(c);
        }else{
            if ((res = check_separators(c)) == -1){
                printf("error\n");
                return 1;
            }else if(res == 1){
                printf("Unbalanced separators\n");
                return 0;
            }
        }
    }
    if (pos == 0){
        printf("Balanced separators\n");
    }else{
        printf("Unbalanced separators\n");
    }
    return 0;
}

void in_comment(void)
{
    int last_c;
    int c;

    last_c = getchar();
    while ((c = getchar()) != EOF){
        if (last_c == '*' && c == '/'){
            return;
        }
        last_c = c;
    }
}

void in_string(char edge)
{
    int c;

    while((c = getchar()) != EOF){
        if (c == '\\'){
            getchar();
        }
        if (c == edge){
            return;
        }
    }
}

/* return 0 on success and -1 on error */
int pushsep(char c)
{
    extern char separatorstack[MAXNESTED];
    extern int pos;

    if (pos >= MAXNESTED){
        return -1;
    }else{
        separatorstack[pos++] = c;
    }
    return 0;
}

/* return a char value on success and EOF if the stack is empty */
int popsep(void)
{
    extern char separatorstack[MAXNESTED];
    extern int pos;

    if (pos <= 0){
        return EOF;
    }else{
        return separatorstack[--pos];
    }
}

/* 
* return 0 is everything is ok,
* return 1 if an unbalance separator is detected and
* return -1 on error  
*/
int check_separators(int c)
{
    if (c == '(' || c == '[' || c == '{'){
        if (pushsep(c) == -1){
            return -1;
        }
    }else if (c == ')'){
        if (popsep() != '('){
            return 1;
        }
    }else if (c == ']'){
        if (popsep() != '['){
            return 1;
        }
    }else if (c == '}'){
        if (popsep() != '{'){
            return 1;
        }
    }
    return 0;
}

Compilation and run:

$ gcc main.c
$ ./a.out
()[[{()(){}}(([][]))]]
Balanced separators
((){}()
Unbalanced separators
(}){}[]
Unbalanced separators

Notes:

  • We make use of the structure of the previous solution 1-23. We just check the balance of the separators with check_separators whenever we are outside strings, constant characters and comments.

  • A stack data structure is implemented with separatorstack and pos global variables and pushsep and popsep functions. Due to its features, its the best data structure to check for unbalanced separators.

  • This is the first exercise where we use global variables. Until now, variables were local; they are defined inside a block {}, they came into existence only when the block is entered and dissapear when the block is exited. Global variables are defined outside any block, can be used in any block (previous declaration) and remain in existence after the block exited.

  • In general, it is best to avoid the use of global variable and use only when you really need them. This is because global variables introduce side effects in functional programming.

Note

Declaration just tell the compiler that the variable exist so that it can be used. All variables and functions must be declared before usage.

Definition is where memory is set aside for the variable. It also declares the variable to the compiler.

Note

One Definition Rule: One and only one definition of every function or variable is required to appear in the entire program (multiple declarations are allowed though)

Note

Local variables are always defined, they cannot be declared.

Global variables may be declared using extern keyword.

Functions may be declared by ommiting its body {}.