Exercise 5-19. Modify
undclso 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 dcl —
undclis the inverse ofdcl; 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