Warning! This site is under development.
This class will be recorded! Its use will be limited to members of the class. Please do not share with others.
Approximate overview
make for the time being$@, $^, and $< are the big ones)..PHONY. These provide information
to Make about a variety of issues, in this case, targets that aren’t
themselves files (e.g., all or check).We have just scratched the surface of Make. As time progresses, you’ll find more command-line flags to do, more options, etc. Here are some lines I never remember but find useful.
md := $(shell ls *.md 2>/dev/null) # Lists all files matching *.md
# sending error messages to /dev/null
html := $(md:%.md=%.html) # Converts .md to .html
default: $(html)
With some references, do you think you can write your own Makefiles?
Yeah, maybe. Or I’ll ask Sam for help.
Our goal: List many of the important things that the C preprocessor does and why that feature might be included.
Five minutes in small groups, then back to the class as a whole.
Feature / purpose.
Note: If we want to see what the C preprocessor does, we can use
cc -E.
E.g.,
#define STRLEN 32
char str[STRLEN];
becomes
char str[32];
This is useful because?
Note: There are predefined constant. DATE gives you the current date in some horrendous form. The date is translated at compile time.
Detour: const int STRLEN = 32 is also a constant.
E.g.,
#define DOUBLE(X) 2*(X)
y = DOUBLE(3);
becomes
y = 2*(3);
Why have macros?
#includeE.g.,
# foo.h
int foo (double bar);
# baz.c
#include "foo.h"
....
Becomes
#baz.c
# 1 foo.h 1
int foo (double bar);
# 2 baz.c 2
Why?
For example,
#ifdef DEBUG
fprintf (stderr, "I reached point 42 of the code\n");
#endif
Becomes nothing if DEBUG is not defined
# line 100
Becomes what’s in the ifdef if DEBUG is defined
fprintf (stderr, "I reached point 42 of the code\n");
Where do those variables come from?
Sam dislikes “MMMM DD, YYY” because it’s medium / small / large.
Sam really dislikes “MM/DD/YYY” because it’s ambigious. Most of Europe uses “DD/MM/YYYY” What is 3/12/2021?
Sam prefers YYYY-MM-DD because it’s unambiguous and it sorts nicely.
class-2020-05-12.txt
class-2021-03-04.txt
class-2021-04-29.txt
This break inserted at its approximate location. Its precise location will depend on a variety of other factors.
#define CONSTANT value
cc -DCONSTANT=value;
#ifndef STRLEN
#define STRLEN 32
#endif
This is good practice because it lets the program work “as is”, but also allows someone to easily reconfigure it for their special circumstances.
Note that the compilation flags are often done in the makefile
CFLAGS = -DCONSTANT=value
#includeTakes a given file and shoves its contents into your code.
For .h files, those are normally declarations.
For .c files, those are normally code.
Header files are not libraries. Although the #include <math.h> tells us
to include the declarations of the math libraries, it does not
(necessarily) tell the C compiler to include their code.
This is testfoo.c.
#include "foo.h"
#include <stdio.h>
int
main (int argc, char *argv[])
{
double d = 99.5;
int i = foo(d);
printf ("foo (%lf) = %d\n", d, i);
return 0;
} // int
This is foo.h
int foo (double bar);
This is foo.c
#include "foo.h"
int
foo (double bar)
{
return (int) bar;
} // foo
What happens if I type the following?
$ cc testfoo.c -o testfoo
$ $ cc testfoo.c -o testfoo
Undefined symbols for architecture x86_64:
"_foo", referenced from:
_main in testfoo-9c357b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
What does this error message mean?
foo function because it’s not in
testfoo.c nor any of the “standard” places it looks for library
code.NOTE: Even though I did an include of foo.h, I did not get access
to the code in foo.c.
TRADITIONALLY, including <math.h> didn’t give you access to libmath.
You had to add the -lm flag.
.c vs .h files..h files are almost always just declarations.c files can be code.The following compiles directly because it includes the code for
the foo function.
#include "foo.c"
#include <stdio.h>
int
main (int argc, char *argv[])
{
double d = 99.5;
int i = foo(d);
printf ("foo (%lf) = %d\n", d, i);
return 0;
} // int
Why not include a .c file?
.c file, we’ll likely get a conflict..c files unless we are doing clever hacks.This is testfoo3.c
#include "foo.h"
#include "foo.c"
#include <stdio.h>
int
main (int argc, char *argv[])
{
double d = 99.5;
int i = foo(d);
printf ("foo (%lf) = %d\n", d, i);
return 0;
} // int
This is foo.c
#include "foo.h"
int
foo (double bar)
{
return (int) bar;
} // foo
What will happen when we try to compile and run testfoo3.c?
foo.h twice, once directly and once indirectly.So I’m going to change foo.h slightly, adding a type declaration.
int foo (double bar);
typedef struct pair
{
int x;
int y;
} pair;
What types did I just name or create?
pairstruct pairIf I had written
typedef struct pear
{
int x;
int y;
} apple;
I would have created
struct pearappleIf we include the new foo.h twice, we have two identical type
declaration. The C compiler generally does not like that.
$ cc testfoo3.c -o testfoo
In file included from testfoo3.c:2:
In file included from ./foo.c:1:
./foo.h:6:16: error: redefinition of 'pair'
typedef struct pair
^
testfoo3.c:1:10: note: './foo.h' included multiple times, additional include site here
#include "foo.h"
^
./foo.c:1:10: note: './foo.h' included multiple times, additional include site here
#include "foo.h"
^
./foo.h:6:16: note: unguarded header; consider using #ifdef guards or #pragma once
typedef struct pair
^
In file included from testfoo3.c:2:
In file included from ./foo.c:1:
./foo.h:10:5: error: typedef redefinition with different types ('struct (anonymous
struct at ./foo.h:6:16)' vs 'struct pair')
} pair;
^
testfoo3.c:1:10: note: './foo.h' included multiple times, additional include site here
#include "foo.h"
^
./foo.c:1:10: note: './foo.h' included multiple times, additional include site here
#include "foo.h"
^
./foo.h:10:5: note: unguarded header; consider using #ifdef guards or #pragma once
} pair;
^
2 errors generated.
We sometimes inadvertently include the same header file more than once, and we should try avoid that.
#ifndef __HEADER_H__
#define __HEADER_H__
#endf // __HEADER_H__
The preprocessor is not smart about includes, so we have to be smarter about includes.
A macro is an instruction for the preprocessor that gives a pattern and a replacement.
We have constant macros
#define STRLEN 32
We also have function macros, which are parameterized
#define DOUBLE(X) X*2
The preprocessor takes the the text of the parameter to a function macro and substitutes it into the body of that macro, putting that new code into the place of the MACRO “call”.
#define DOUBLE(X) 2*X
#include <stdio.h>
int
main (int argc, char *argv[])
{
printf ("double (3) is %d\n", DOUBLE (3));
return 0;
} // main
After preprocessing
int
main (int argc, char *argv[])
{
printf ("double (3) is %d\n", 2*3);
return 0;
}
If I compile and run, I should get “double (3) is 6” And I do.
Modified
#define DOUBLE(X) 2*X
#include <stdio.h>
int
main (int argc, char *argv[])
{
printf ("double (2+1) is %d\n", DOUBLE(2+1));
return 0;
} // main
What’s the output going to be?
double (2+1) is 5
Why?
double (1+2) is 4
Goal: Compute x^n mod m. Assume x < m and m < sqrt(LONG_MAX).
Obvious solution:
Can we do a more efficient solution?