Roger Costello
Roger Costello

Reputation: 3209

Why don't the C files that I create to support my main program inherit includes and defines from main?

I am going through the C Programming Language book by Kernighan and Ritchie. On pages 76-79 is code for a reverse Polish calculator. I decided to try it out, so I put the main code in a file named calculator.c, the code for the stack in a file named stack.c, the code for getting the operands in a file named getop.c, and the code for getting input from the user in a file named getch.c I show the code below. I then opened a command window and typed this gcc command:

gcc calculator.c stack.c getop.c getch.c -o calculator

I got many errors.

I have several questions:

  1. calculator.c has #include <stdio.h> so why do I need it also in the other files; don't they inherit the include?

  2. calculator.c also has #define NUMBER '0' so why do I get an error in getop.c saying 'NUMBER' is undeclared; again, doesn't it inherit the define?

  3. Is there some command that I can enter at the command line to direct gcc to use the include and define that is in calculator.c with the other files? Or, do I need to repeat the include and define in the other files? What do C experts do?

Here is calculator.c

#include <stdio.h>
#include <math.h>   // for atof() //

#define MAXOP  100  /* max size of operand or operator */
#define NUMBER  '0'  /* signal that a number was found */

int getop(char []);
void push(double);
double pop(void);

/* reverse Polish calculator */
int main()
{
    int type;
    double op2;
    char s[MAXOP];
    
    while ((type = getop(s)) != EOF) {
        switch (type) {
        case NUMBER:
            push(atof(s));
            break;
        case '+':
           push(pop() + pop());
           break;
        case '-':
           op2 = pop();
           push(pop() - op2);
           break;
        case '/':
           op2 = pop();
           if (op2 != 0.0)
              push(pop() / op2);
           else
              printf("error: zero divisor\n");
           break;
        case '\n':
           printf("\t%.8g\n", pop());
           break;
        default:
           printf("error: unknown command %a\n", s);
           break;
        }
    }
    return 0; 
}

Here is stack.c

#define MAXVAL  100  /* maximum depth of the val stack */

int sp = 0;          /* next free stack position */
double val[MAXVAL];  /* value stack */

/* push: push f onto value stack */
void push (double f)
{
    if (sp < MAXVAL)
       val[sp++] = f;
    else
       printf("error: stack full, can't push %g\n", f);
}

/* pop: pop and return top value from stack */
double pop(void)
{
    if (sp > 0)
       return val[--sp];
    else {
       printf("error: stack empty\n");
       return 0.0;
    }
}

Here is getop.c

#include <ctype.h>

int getch(void);
void ungetch(int);

/* getop: get next operator or numeric operand */
int getop(char s[])
{
   int i, c;
   
   while ((s[0] = c = getch()) == ' ' || c == '\t')
      ;
   s[1] = '\0';
   if (!isdigit(c) && c != '.')
      return c;   /* not a number */
   i = 0;
   if (isdigit(c))   /* collect integer part */
      while (isdigit(s[++i] = c = getch()))
         ;
   if (c == '.')    /* collect fraction part */
      while (isdigit(s[++i] = c = getchar()))
         ;
   s[i] = '\0';
   if (c != EOF)
      ungetch(c);
   return NUMBER;
}

Here is getch.c

#define BUFSIZE 100

char buf[BUFSIZE];   /* buffer for ungetch */
int bufp = 0;        /* next free position in buf */

int getch(void)  /* get a (possibly pushed back) character */
{
    return (bufp > 0 ? buf[--bufp] : getchar();
}

void ungetch(int c)  /* push characters back on input */
{
   if (bufp >= BUFSIZE)
      printf("ungetch: too manycharacters\n");
   else
      buf[bufp++] = c;
}

Upvotes: 2

Views: 83

Answers (2)

August Karlstrom
August Karlstrom

Reputation: 11377

Welcome to the world of modular programming. A module in C can be realized by means of a header file (with filename suffix .h) and an implementation file (with filename suffix .c). The header file contains the exported features that can be used in other modules.

A typical C compiler first translates each .c file to binary code (an object file) independently of the other .c files. Therefor each .c file must include the declarations of the features it uses from other modules. After that the object files are linked into an executable program.

In your case you need to create the files stack.h, getopt.h and getch.h with the following content:

stack.h:

void push(double f);
double pop(void);

getopt.h:

#define NUMBER '0'

int getch(void);
void ungetch(int);

getch.h:

int getch(void);    
void ungetch(int c);

It is standard practice in C to use the preprocessor to prevent a header file from being included more than once in the same translation unit. It is also a good idea to prefix each exported identifier with a module prefix, which is typically the filename. With this approach you prevent name clashes and can instantly tell where an included identifier comes from. By following these recommendations your header files will look like this:

stack.h:

#ifndef STACK_H
#define STACK_H

void stack_push(double f);
double stack_pop(void);

#endif

getopt.h:

#ifndef GETOPT_H
#define GETOPT_H

#define GETOPT_NUMBER '0'

int getopt_getch(void);
void getopt_ungetch(int);

getch.h:

#ifndef GETCH_H
#define GETCH_H

int getch_getch(void);    
void getch_ungetch(int c);

#endif

In calculator.c you then include the header files:

#include "getch.h"
#include "getopt.h"
#include "stack.h"

Upvotes: 0

Acorn
Acorn

Reputation: 26186

calculator.c has #include <stdio.h> so why do I need it also in the other files; don't they inherit the include?

No, each .c file is an independent "translation unit". There is no "automatic module discovery" support in C.

In truth, writing #include does not do what you probably think it does: it is just copy-pasting the content of the header file, but that is not equivalent to the usual "module system" from other languages where the compiler finds out the dependencies (Python, for example, will try to find the corresponding module when you write import); instead, in C you typically have more translation units compiled independently compared to other languages.

calculator.c also has #define NUMBER '0' so why do I get an error in getop.c saying 'NUMBER' is undeclared; again, doesn't it inherit the define?

See above.

Is there some command that I can enter at the command line to direct gcc to use the include and define that is in calculator.c with the other files? Or, do I need to repeat the include and define in the other files? What do C experts do?

Yes, you are supposed to repeat the declarations (or whatever you need to make other translation units aware of the functions etc). That is where #include comes from: it copy-pastes another file into the current file. So what is usually done is write a header file with the declarations and #defines that you need, and then it is #included in several .c files (or other header files).

Upvotes: 3

Related Questions