2

I've been beating my head trying to understand the second assignment for pset 3. It states in part:

Notice that find.c calls search, a function declared in helpers.h. Unfortunately, we forgot to implement that function fully in helpers.c! (To be sure, we could have put the contents of helpers.h and helpers.c in find.c itself....

When ready to check the correctness of your program, try running the command below.

./generate 1000 50 | ./find 2008"

I think I understand that helpers.h is a file that declares two functions rather than putting them in the header of find.c. However, how does find.c know to look in helpers.c for the search routine? I see no connection.

Also, I thought .c files were to be compiled and yet nowhere does it tell us to compile helpers.c.

1 Answer 1

3

Looking at the distribution code for find.c, we can see this line near the top of the file:

#include "helpers.h"

That header file, helpers.h, contains only the prototypes for your search and sort functions; the actual implementations are in helpers.c.1 So how do we get from find.c to helpers.c without the line #include helpers.c somewhere in the mix?

The answer is that it all happens thanks to the make utility -- and more specifically, the Makefile that's included in the find directory. The Makefile contains instructions for make, as explained in the pset specs:

Notice further that you just compiled a program comprising not one but two .c files: helpers.c and find.c. How did make know what to do? Well, again, open up Makefile to see the man behind the curtain. The relevant lines appear below.

find: find.c helpers.c helpers.h
    clang -ggdb -std=c99 -Wall -Werror -o find find.c helpers.c -lcs50 -lm

Per the dependencies implied above (after the colon), any changes to find.c, helpers.c, or helpers.h will compel make to rebuild find the next time it’s invoked for this target.

How the Makefile works is explained in a recommended reading from the beginning of the specs:

This makefile contains two types of lines. The lines appearing flush left are dependency lines. The lines preceded by a tab are executable lines, which can contain any valid UNIX command. A dependency line says that some file is dependent on some other set of files. For example, main.o: main.c util.h says that the file main.o is dependent on the files main.c and util.h. If either of these two files changes, the following executable line(s) should be executed to recreate main.o.

In our case, what links the internal implementation of the header file prototypes to find.c at compile time is that:

  1. The dependency line of our Makefile shows that find depends on helpers.c;
  2. The executable line that follows provides that same dependency as a command-line argument to the compiler, clang.

Why isn't there a helpers.h argument, if it's listed as a dependency? Because it's already there -- we included it in find.c -- we just need to remind make that it's there, because make looks for changes in the dependencies before clang is executed and the #include directive is processed.

So why don't you have to #include helpers.c? The straight answer is that all you are required to provide in the source file, at a minimum, is prototypes for the functions you call. Notice you didn't #include <stdio.c> either, just a header (.h) file!

As far as find.c is concerned, the only functions you need are search and sort, which are prototyped in helpers.h. You don't really need to know the details of how search is implemented in order to call it -- in fact, you could modify helpers.c all you wanted and as long as the prototypes in helpers.h didn't change, you wouldn't have to make any changes to find.c.

Is this the only way to do it? No; you could put all of the implementation from headers.c into headers.h instead, remove references to headers.c from the Makefile, and it would still work. But the convention when writing C code is to do it this way; if you're interested in the reasons, there are some discussions on other Stack Exchange sites that you could look up -- I'll start you off with this one: Why do we need to write a header file?

4
  • Thank you for your comment. However, it doesn't answer the question, how does find call helpers.c When I make find, I don't see a helpers file. I only see helpers.c and helpers.h. The search function is written in helpers.c yet there is no helpers file when compiled so how does find use helpers.c? Is there something inherent in C programming where the header files (helper.h) automatically point to a .c file (helpers.c)? Thanks.
    – Chess Dad
    Commented Aug 19, 2014 at 19:45
  • @ChessDad Updated. Hope this fills in the blanks for you!
    – Air
    Commented Aug 19, 2014 at 21:04
  • Thank you for your replies. This does help. One more question though. I may have overlooked a reference. I don't recall in the lectures, shorts, etc. ever mentioning a header file (.h). Can you point me to the reference? Seems confusing to include something like this and not discuss it during the lectures, shorts, etc. Thanks again.
    – Chess Dad
    Commented Sep 5, 2014 at 20:30
  • @ChessDad Try page 16 from howstuffworks.com; it's not included in the recommended reading, but comes right before the page on Makefiles. There might be some more discussion in a short or walkthrough, and I'm sure it's covered in one or both of the textbooks, but I don't have a reference handy. You could ask another question, if you like, to see if anyone knows of a place where header files are covered in the course material.
    – Air
    Commented Sep 5, 2014 at 20:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .