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 ofmain
. This mean that every program must have amain
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
andfor
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 asc
. 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()
returnEOF
. 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 either0
or1
)
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 anint
(or can be casted to anint
without problem) because it is used as the error return value of functions that returnint
.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
tomygetname
because there is a previous declaration ofgetline
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 usegetline
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, andn
is not used for anything. Callingfree
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 wayreverse
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
andpos
global variables andpushsep
andpopsep
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 {}
.