K&R C Programs Exercise 5-19

Exercise 5-19. Modify undcl so that it does not add redundant parentheses to expressions.

K&R’s undcl (Section 5.12) converts English descriptions back into C declarations. The original adds parentheses around every pointer-to-function construct, even when they are not needed. A pointer to a function needs parentheses — int (*fp)(); a simple pointer does not — int *p, not int (*p). The fix: only emit parentheses when the next token after pointer to is function or array.

Solution

/* K&R Exercise 5-19 — undcl without redundant parentheses
 * Compile: gcc -ansi -Wall ex5-19.c -o undcl19
 * Input lines: name description basetype
 * Example:  x pointer to pointer to char
 *           fp pointer to function returning int */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXTOKEN 100
#define MAXTOKS  100

static char tokens[MAXTOKS][MAXTOKEN];
static int  ntokens;

static void tokenize(char *s)
{
    ntokens = 0;
    while (*s) {
        while (*s == ' ' || *s == '\t') s++;
        if (!*s) break;
        char *p = tokens[ntokens++];
        while (*s && *s != ' ' && *s != '\t') *p++ = *s++;
        *p = '\0';
    }
}

int main(void)
{
    char line[500], temp[500];
    int i;

    while (fgets(line, sizeof line, stdin)) {
        /* strip newline */
        line[strcspn(line, "\n")] = '\0';
        tokenize(line);
        if (ntokens < 2) continue;

        /* tokens[0] = name, tokens[1..ntokens-2] = description words,
         * tokens[ntokens-1] = base type */

        /* Build declaration right-to-left:
         * Start with name, wrap each modifier */
        char cur[500];
        strcpy(cur, tokens[0]);       /* name */
        char *basetype = tokens[ntokens-1];

        for (i = 1; i < ntokens - 1; ) {
            if (strcmp(tokens[i], "pointer") == 0 &&
                i+1 < ntokens-1 && strcmp(tokens[i+1], "to") == 0) {
                i += 2;
                /* Look ahead: does the next token start "function" or "array"? */
                int needparens = (i < ntokens-1 &&
                    (strcmp(tokens[i], "function") == 0 ||
                     strcmp(tokens[i], "array") == 0));
                if (needparens)
                    sprintf(temp, "(*%s)", cur);
                else
                    sprintf(temp, "*%s", cur);
                strcpy(cur, temp);
            } else if (strcmp(tokens[i], "function") == 0 &&
                       i+1 < ntokens-1 && strcmp(tokens[i+1], "returning") == 0) {
                i += 2;
                sprintf(temp, "%s()", cur);
                strcpy(cur, temp);
            } else if (strcmp(tokens[i], "array") == 0) {
                /* collect the size token if present */
                i++;
                char sz[32] = "";
                if (i < ntokens-1 && tokens[i][0] == '[') {
                    strcpy(sz, tokens[i++]);
                }
                if (i < ntokens-1 && strcmp(tokens[i], "of") == 0) i++;
                sprintf(temp, "%s%s", cur, sz[0] ? sz : "[]");
                strcpy(cur, temp);
            } else {
                i++;
            }
        }
        printf("%s %s\n", basetype, cur);
    }
    return 0;
}

Test It

printf "p pointer to int\nfp pointer to function returning int\nap array of pointer to char\n" | ./undcl19

Sample Output

int *p
int (*fp)()
char *ap[]

*p has no parentheses (it’s a plain pointer); (*fp) does (it’s a pointer to function — parentheses are required by C syntax).

What This Exercise Teaches

  • Lookahead for parenthesisation — the decision to add parens depends on what comes next in the description, not on the current token alone
  • Precedence encoding — parentheses encode precedence: * binds less tightly than [] and (), so a pointer to an array or function needs parens; a plain pointer does not
  • Inverse of dclundcl is the inverse of dcl; the two programs together demonstrate that C declaration syntax is fully reversible

Set Up Your C Environment

← Exercise 5-18  | 
Chapter 5 Solutions  | 
Exercise 5-20 →

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>