Appendix: Basic Programming Elements

Diomidis Spinellis
Department of Management Science and Technology
Athens University of Economics and Business
Athens, Greece
dds@aueb.gr

A Complete Program

/*      $NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $ */

/*
 * Copyright (c) 1989, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT(
"@(#) Copyright (c) 1989, 1993\n\
        The Regents of the University of California.  All rights reserved.\n");
#endif /* not lint */

#ifndef lint
#if 0
static char sccsid[] = "@(#)echo.c      8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: echo.c,v 1.7 1997/07/20 06:07:03 thorpej Exp $");
#endif
#endif /* not lint */

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

int     __Pmain ((intchar *[]));

main(argc, argv)
        int argc;
        char *argv[];
{
        int nflag;

        /* This utility may NOT do getopt(3) option parsing. */
        if (*++argv && !strcmp(*argv, "-n")) {
                ++argv;
                nflag = 1;
        }
        else
                nflag = 0;

        while (*argv) {
                (void)printf("%s", *argv);
                if (*++argv)
                        putchar(' ');
        }
        if (!nflag)
                putchar('\n');
        exit(0);
}
Important issues:

Expand: A Larger Example

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

/*
 * expand - expand tabs to equivalent spaces
 */
int     nstops;
int     tabstops[100];

static  void    getstops __P((char *));
        int     main __P((intchar **));
static  void    usage __P((void));

int
main(argc, argv)
        int argc;
        char *argv[];
{
        int c, column;
        int n;

        /* handle obsolete syntax */
        while (argc > 1 && argv[1][0] && isdigit(argv[1][1])) {
                getstops(&argv[1][1]);
                argc--; argv++;
        }

        while ((c = getopt (argc, argv, "t:")) != -1) {
                switch (c) {
                case 't':
                        getstops(optarg);
                        break;
                case '?':
                default:
                        usage();
                        /* NOTREACHED */
                }
        }
        argc -= optind;
        argv += optind;

        do {
                if (argc > 0) {
                        if (freopen(argv[0], "r"stdin) == NULL) {
                                perror(argv[0]);
                                exit(1);
                        }
                        argc--, argv++;
                }
                column = 0;
                while ((c = getchar()) != EOF) {
                        switch (c) {
                        case '\t':
                                if (nstops == 0) {
                                        do {
                                                putchar(' ');
                                                column++;
                                        } while (column & 07);
                                        continue;
                                }
                                if (nstops == 1) {
                                        do {
                                                putchar(' ');
                                                column++;
                                        } while (((column - 1) % tabstops[0]) != (tabstops[0] - 1));
                                        continue;
                                }
                                for (n = 0; n < nstops; n++)
                                        if (tabstops[n] > column)
                                                break;
                                if (n == nstops) {
                                        putchar(' ');
                                        column++;
                                        continue;
                                }
                                while (column < tabstops[n]) {
                                        putchar(' ');
                                        column++;
                                }
                                continue;

                        case '\b':
                                if (column)
                                        column--;
                                putchar('\b');
                                continue;

                        default:
                                putchar(c);
                                column++;
                                continue;

                        case '\n':
                                putchar(c);
                                column = 0;
                                continue;
                        }
                }
        } while (argc > 0);
        exit(0);
}

static void
getstops(cp)
        char *cp;
{
        int i;

        nstops = 0;
        for (;;) {
                i = 0;
                while (*cp >= '0' && *cp <= '9')
                        i = i * 10 + *cp++ - '0';
                if (i <= 0 || i > 256) {
bad:
                        fprintf(stderr"Bad tab stop spec\n");
                        exit(1);
                }
                if (nstops > 0 && i <= tabstops[nstops-1])
                        goto bad;
                tabstops[nstops++] = i;
                if (*cp == 0)
                        break;
                if (*cp != ',' && *cp != ' ')
                        goto bad;
                cp++;
        }
}

static void
usage()
{
        (void)fprintf (stderr"usage: expand [-t tablist] [file ...]\n");
        exit(1);
}

Declarations and visibility

int     nstops;
int     tabstops[100];

static  void    getstops (char *);
        int     main (intchar **);
static  void    usage(void);
Issues:

Functions and Global Variables

Functions can help you identify a program's major parts.

To find what a function does:

while Loops, Conditions, and Blocks

        while ((c = getopt (argc, argv, "t:")) != -1) {
                switch (c) {
                case 't':
                        getstops(optarg);
                        break;
                case '?':
                default:
                        usage();
                        /* NOTREACHED */
                }
        }
Issues:

switch Statements

for Loops

Loop Control Statements

Character Expressions

Boolean Expressions

goto Statements

Refactoring in the Small

Illustrative Example

        if (*cp == 0)
                break;
        if (*cp != ',' && *cp != ' ')
                goto bad;
        cp++;
Changed into:
        if (*cp == 0)
                break;
        else if (*cp != ',' && *cp != ' ')
                goto bad;
        else
                cp++;

Refactoring Example

Original Code

(worms game)
op = &(!x ? (!y ? upleft : (y == bottom ? lowleft : left)) :
(x == last ? (!y ? upright : (y == bottom ? lowright : right)) :
(!y ? upper : (y == bottom ? lower : normal))))[w->orientation];
What is going one here?

More Readable Representation

(Formatted as cascading if-else)
op = &(
!x ? (
    !y ? 
        upleft
    : ( y == bottom ? 
        lowleft
    :
        left
    )
) : ( x == last ? (
    !y ? 
        upright
    : ( y == bottom ? 
        lowright
    :
        right
    )
) : (
    !y ?
        upper
    : ( y == bottom ?
        lower
    :
        normal
    )
)
))[w->orientation];

Re-implementation

struct options *locations[3][3] = {
        {upleft,  upper,  upright},
        {left,    normal, right},
        {lowleft, lower, lowright},
};
int xlocation, ylocation;

if (x == 0)
        xlocation = 0;
else if (x == last)
        xlocation = 2;
else
        xlocation = 1;
if (y == 0)
        ylocation = 0;
else if (y == bottom)
        ylocation = 2;
else
        ylocation = 1;
op = &(locations[ylocation][xlocation])[w];

Other Implementation

(Suggested by Guy Steele)
op =
  &(        !y ? (!x ?  upleft : x!=last ?  upper :  upright ) :
     y!=bottom ? (!x ?    left : x!=last ? normal :    right ) :
                 (!x ? lowleft : x!=last ?  lower : lowright )
   )[w->orientation];

Other Refactoring Changes

Assignment Expressions

Bit Manipulation

Reading Control Structures

In structured programs:

Further Reading

Exercises and Discussion Topics

  1. Experiment to find-out how your C, C++, and Java compilers deal with uninitialized variables. Outline your results and propose an inspection procedure for locating uninitialized variables.
  2. Look in the course's source code repository for programs that do not verify the result of library calls. Propose practical fixes.
  3. Examine the visibility of functions and variables in programs in your environment. Can it be improved (made more conservative)?
  4. Discover how the editor you are using can identify matching braces and parentheses. If it can not, consider to start using another editor.
  5. Provide five examples from the course's source code collection where the code structure can be improved to make it more readable.
  6. You can find tens of intentionally unreadable C programs at the International Obfuscated C Code Contest web site (http://www.ioccc.org). Most of them use several layers of obfuscation to hide their algorithms. See how gradual code changes can help you untangle their code. If you are not familiar with the C preprocessor try to avoid programs with a large number of #define lines.
  7. The Perl language mandates the use of braces for all its control structures. Comment on how this affects the readability of Perl programs.
  8. The code body of switch statements in the course's source code collection is formatted differently from the other statements. Express the formatting rule used, and explain its rationale.
  9. The for statement in the C language family is very flexible. Examine the source code provided to create a list of ten different uses.
  10. Locate ten occurrences of break and continue in the source code provided with the course. For each case indicate the point where execution will transfer after the corresponding statement is executed, and explain why the statement is used. Do not try to understand in full the logic of the code; simply provide an explanation based on the statement's use pattern.
  11. Find, simplify, and reason about five non-trivial boolean expressions in the source-code base. Do not spend time on understanding what the expression elements mean, concentrate on the conditions that will make the expression become true or false. Where possible, identify and utilize the properties of short-circuit evaluation.
  12. Study Section 2.11 from the book. Provide a proof about the correctness of a sort algorithm, found in the course's soruce repository.