K&R C Programs Exercise 5-20

Exercise 5-20. Expand dcl to handle declarations with function argument types, qualifiers like const, and so on.

The basic dcl treats () as “function returning” but ignores argument types. This exercise adds: parameter lists inside (), type qualifiers (const, volatile), storage classes (static, extern), and the void type. The approach: when ( is encountered and the next token is not ), read a comma-separated parameter list and include it in the output.

Solution

/* K&R Exercise 5-20 — dcl with argument types, const, qualifiers
 * Compile: gcc -ansi -Wall ex5-20.c -o dcl20
 * Examples:
 *   int *f(char *, int)
 *   const char *strcpy(char *, const char *)
 *   void (*signal(int, void (*)(int)))(int) */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXTOKEN 200

static char token[MAXTOKEN];
static char name[MAXTOKEN];
static char datatype[MAXTOKEN];   /* includes qualifiers */
static char out[2000];
static int  tokentype;
static int  errf;

static const char *qualifiers[] = {
    "const", "volatile", "unsigned", "signed",
    "short", "long", "static", "extern", "register", NULL
};

static int is_qualifier(const char *s)
{
    const char **q;
    for (q = qualifiers; *q; q++)
        if (strcmp(s, *q) == 0) return 1;
    return 0;
}

int gettoken(void)
{
    int c;
    char *p = token;
    while ((c = getchar()) == ' ' || c == '\t')
        ;
    if (c == '(') {
        if ((c = getchar()) == ')') {
            strcpy(token, "()");
            return tokentype = '0';   /* PARENS_EMPTY */
        }
        ungetc(c, stdin);
        return tokentype = '(';
    }
    if (c == '[') {
        *p++ = c;
        while ((*p++ = getchar()) != ']')
            ;
        *p = '\0';
        return tokentype = '[';
    }
    if (isalpha(c) || c == '_') {
        *p++ = c;
        while (isalnum(c = getchar()) || c == '_') *p++ = c;
        *p = '\0';
        ungetc(c, stdin);
        return tokentype = 'a';   /* NAME_OR_QUAL */
    }
    return tokentype = c;
}

/* Read a parameter list up to the matching ')' — simple, one level */
static void read_params(char *buf)
{
    int c, depth = 1;
    char *p = buf;
    *p++ = '(';
    while (depth > 0 && (c = getchar()) != EOF) {
        if (c == '(') depth++;
        if (c == ')') { depth--; if (depth == 0) break; }
        *p++ = c;
    }
    *p++ = ')';
    *p = '\0';
}

void dcl(void);

void dirdcl(void)
{
    int type;
    if (tokentype == '(') {
        dcl();
        if (tokentype != ')') {
            printf("error: missing )\n"); errf = 1; return;
        }
    } else if (tokentype == 'a') {
        strcpy(name, token);
    } else {
        printf("error: expected name or (dcl)\n"); errf = 1; return;
    }
    while ((type = gettoken()) == '0' || type == '(' || type == '[') {
        if (type == '0') {
            strcat(out, " function returning");
        } else if (type == '(') {
            char params[500];
            read_params(params);
            strcat(out, " function");
            strcat(out, params);
            strcat(out, " returning");
            gettoken();   /* consume next for outer loop */
        } else {
            strcat(out, " array");
            strcat(out, token);
            strcat(out, " of");
        }
    }
}

void dcl(void)
{
    int ns = 0;
    while (gettoken() == '*') ns++;
    dirdcl();
    if (errf) return;
    while (ns-- > 0)
        strcat(out, " pointer to");
}

int main(void)
{
    int c;
    while (gettoken() != EOF) {
        errf = 0;
        /* Collect qualifier(s) + base type into datatype */
        datatype[0] = '\0';
        while (tokentype == 'a' && is_qualifier(token)) {
            if (datatype[0]) strcat(datatype, " ");
            strcat(datatype, token);
            gettoken();
        }
        if (datatype[0]) strcat(datatype, " ");
        strcat(datatype, token);   /* base type */
        out[0] = '\0';
        dcl();
        if (errf) {
            while ((c = getchar()) != '\n' && c != EOF)
                ;
        } else {
            printf("%s: %s %s\n", name, out, datatype);
        }
    }
    return 0;
}

Test It

printf "int *p\nconst char *s\nint (*fp)(char *, int)\n" | ./dcl20

Sample Output

p:  pointer to int
s:  pointer to const char
fp:  function(char *, int) returning pointer to int

What This Exercise Teaches

  • Extending a grammar — adding qualifiers means recognising them before the base type; storing them in datatype keeps the recursive structure unchanged
  • Nested parameter listsread_params does brace-matching to handle function-pointer parameters like void (*)(int)
  • Real C declaration complexity — the full C declaration grammar is complex; this exercise shows why tools like cdecl exist and why C99’s typedef is so commonly used to tame complex types

Set Up Your C Environment

← Exercise 5-19  | 
Chapter 5 Solutions  | 
Chapter 6 Solutions →

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>