practical c issues - university of...

46
Practical C Issues: Preprocessor Directives, Multi-file Development, Makefiles CS449 Fall 2017

Upload: haquynh

Post on 04-Jun-2018

217 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Practical C Issues:!Preprocessor Directives, Multi-file

Development, Makefiles

CS449 Fall 2017

Page 2: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Multi-file Development

Page 3: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Multi-file Development•  Why break code into multiple source files?

–  Parallel development involving multiple authors–  Quicker compilation (only compile modified file)–  Modularity (can reuse object file / library)–  Encapsulation (easier to read / maintain)

•  Use smallest scope to enforce encapsulation–  Avoids polluting global namespace–  Minimizes scope of code to find all uses of symbol

•  But sometimes scopes must cross file boundaries...–  Then same symbol must be declared in all relevant source files

Page 4: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Block / Function Scope

•  Scope: Local (function or block of code)•  Lifetime: Automatic (duration of function)

Static (duration of program)void foo(…) {

int x; // automatic

static int y; // static

x = …

y = …

}

Page 5: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Internal Linkage Scope

•  Scope: File•  Lifetime: Static (duration of program)static int x;

void foo(…) {

x = …

}

Void bar(…) {

x = …

}

Page 6: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

External Linkage Scope

•  Scope: Program (multiple files)•  Lifetime: Static (duration of program)•  extern used to declare vars in other filesFile Aint x;void foo() { … }

File Bextern int x;void foo();

B declares x and foo defined in A

Page 7: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Multi-file Example

a.cint x = 0; int f(int y) {

return x+y;

}

b.c#include <stdio.h> /* Declarations for symbols

defined in other files */ extern int x; int f(int); int main() { x = 1; printf("%d", f(2)); return 0; }

Page 8: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Multi-file Example

thoth$ gcc –c a.c b.c thoth$ gcc a.o b.o

thoth$ ./a.out

3

Page 9: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Pitfall: Missing Definitions

a.c// Missing ‘x’ definition int f(int y)

{

return y;

}

b.c#include <stdio.h> /* Declarations for symbols

defined in other files */ extern int x; int f(int); int main() { x = 1; printf("%d", f(2)); return 0; }

Page 10: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Pitfall: Missing Definitionsthoth$ gcc –c a.c b.c thoth$ gcc a.o b.o

./b.o: In function `main':

b.c:(.text+0x6): undefined reference to `x'

collect2: ld returned 1 exit status

•  Linker cannot find ‘x’ in symbol table when it tries to resolve x in ‘x = 1;’ inside b.c

Page 11: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Pitfall: Missing Declarations

a.cint x = 0;

int f(int y)

{

return x+y;

}

b.c#include <stdio.h> /* Missing declarations for

x and int f(int) */ int main() { x = 1; printf("%d", f(2)); return 0; }

Page 12: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Pitfall: Missing Declarations

thoth$ gcc –c a.c b.c ./b.c: In function ‘main’:

./b.c:5: error: ‘x’ undeclared

•  Compiler must know there is an externally defined ‘x’ and its type is ‘int’ to generate code

Page 13: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Declarations and Definitions•  Definitions: Put into individual C (.c) files

–  Function definitions: ends up in text section of .o file–  Variable definitions: ends up in data section of .o file–  One symbol must be defined in only one C file

•  Declarations: Put into header (.h) filesè #included in any C file using the symbols–  Function declarations–  Variables declarations (using extern)–  Type definitions (e.g. struct definitions)–  #defines

Page 14: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Declarations and Definitions

mymalloc.h// Declarations

void *my_malloc(int size);

void my_free(void *ptr);

•  Why wasn’t head declared in mymalloc.h?

mymalloc.c// Definitions

static void *head;

void *my_malloc(int size)

{

...

}

void my_free(void *ptr)

{

...

}

Page 15: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Including a Header File•  Insert header In each C file using my_malloc, my_free:

main.c#include “mymalloc.h” // insert header int main() {

my_malloc(…); } foo.c #include “mymalloc.h” // insert header int foo() {

my_free(…); }

Page 16: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Compiling Multiple Files•  Compiling:

gcc mymalloc.c main.c foo.c

•  Why not also compile mymalloc.h?A.  Header does not define any symbols

–  Contains only declarations to help compiler–  Nothing to generate an object file out of

(Code / data segment would be empty)–  Hence nothing to compile

Page 17: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Makefiles:!Easy Multi-file Development

Page 18: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Makefile•  A script interpreted by the GNU Make utility to build

projects containing multiple files•  Design Goal: on a source / header file modification,

perform the smallest set of actions required–  By expressing what files depend upon others

•  Composed of a collection of rules which look liketarget:dependencies

action–  That is a single <tab> before action, not spaces!

•  Script invoked by: make<target>–  If no target given, first target in script built by default

Page 19: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Makefilemalloctest: mymalloc.o mallocdriver.o

gcc –o malloctest mymalloc.o mallocdriver.o

mymalloc.o: mymalloc.c mymalloc.h

gcc –c mymalloc.c

mallocdriver.o: mallocdriver.c mymalloc.h gcc –c mallocdriver.c

clean:

rm –f *.o malloctest

Page 20: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Dependency Graph

malloctest

mymalloc.o mallocdriver.o

mymalloc.hmymalloc.c mallocdriver.c

Page 21: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Using a Makefile

thoth$lsMakefile mallocdrv.c mymalloc.c mymalloc.h thoth$makegcc -c mymalloc.c gcc -c mallocdrv.c gcc -o malloctest mymalloc.o mallocdrv.o thoth$makemake: `malloctest' is up to date.

•  Build from scratch

•  Partial build after modifying mymalloc.cthoth$touchmymalloc.cthoth$makegcc -c mymalloc.c gcc -o malloctest mymalloc.o mallocdrv.o

Page 22: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Defining Variables in Makefiles

•  Works like macros (text replacement)•  Syntax: <name> := ... or <name> = ...•  Example:

–  Instead of:malloctest: mymalloc.o mallocdriver.o

gcc –o malloctest mymalloc.o mallocdriver.o

– Can do:OBJECTS = mymalloc.o mallocdriver.o

malloctest: $(OBJECTS)

gcc –o malloctest $(OBJECTS)

Page 23: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Automatic Variables

•  $@: The file name of the target. E.g.:malloctest: $(OBJECTS)

gcc –o $@ $(OBJECTS)

•  $<: The name of the first prerequisite. E.g.:mymalloc.o: mymalloc.c mymalloc.h gcc –c $<

•  $^: The names of all prerequisites. E.g.:malloctest: $(OBJECTS)

gcc –o $@ $^

Page 24: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Pattern Matching•  Character ‘%’ is a wildcard for any string•  Example:%.o: %.c

gcc –c $< -o $@

•  What it means:–  For all targets matching <some string>.o–  Dependency is <that string>.c–  Action is gcc –c <that string>.c –o <that string>.o

•  Rule is used to produce any .o file from .c file

Page 25: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Concise Makefilemalloctest: mymalloc.o mallocdriver.o

gcc -o $@ $^

%.o: %.c

gcc -c $< -o $@

mymalloc.o: mymalloc.h mallocdriver.o: mymalloc.h

clean:

rm -f *.o malloctest

Page 26: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Make Utility Options•  Usage:

make [-f makefile] [options] [targets]

•  -f makefile: Makefile to interpret•  targets: Targets to be built•  Options:

–  <name> = <value>: Define a variable.–  -C <dir>: Change to directory <dir> before building.–  -n: Dry run. Just print commands and don’t execute.–  -d: Debug mode. Print verbose information.

Page 27: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Preprocecessor Directives:!Easy Manipulation of Source Code

Page 28: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#include•  Copies the contents of specified file into current file•  #include <…>: file in standard include location

–  Usually /usr/include •  #include “ ”: file in current directory or specified paths

–  Paths specified using the –I option•  If main.c has following includes:#include <stdio.h> #include “myheader.h”

… and is compiled using gcc –I ~/local/include main.c –  stdio.h under /usr/include –  myheader.h under current directory or ~/local/include

Page 29: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#define

•  Defines macros– Macro: rule that specifies textual replacement

of one string for another•  Often used to assign names to constants#define PI 3.1415926535 #define MAX 10

float f = PI; for(i=0;i<MAX;i++) …

Page 30: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#define•  Good macros are generic

(do not make assumptions about inputs)•  Good:

–  #define MAX(a,b) (a > b) ? a : b –  Only assumes ‘a’ can be compared to ‘b’

•  Not so good:–  #define SWAP(a,b) {int t=a; a=b; b=t;} –  Makes assumption that types are ‘int’

•  Better–  #define SWAP(T,a,b) {T t=a; a=b; b=t;}

Page 31: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#if•  #if <condition known to preprocessor>

// Some code #endif–  Preprocessor emits code between #if directive and

#endif directive to the compiler only if condition is true–  Condition evaluated at preprocessing time

(cf. C if statement is evaluated at execution time)•  What does preprocessor know?

–  Constants (0, 1, 2, “Linux”, “x86”, …)–  Values of #defined variables–  Arithmetic (+, -, *, /, >, <, ==, &&, ||, …)

Page 32: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Example#include <stdio.h> int main()

{ #if 0

printf(“This is not compiled\n”);

I can doodle here when I am bored. #endif

printf(“This is compiled\n”); return 0;

}

Page 33: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Example 2#include <stdio.h> #define LIBRARY_VERSION 7

int main() {

#if LIBRARY_VERSION >= 5

some_function_included_in_version_5(); printf(“This is compiled\n”);

#endif return 0;

}

Page 34: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#else#if <condition1>

// Emitted if condition1 is true…

#elif <condition2>// Emitted if condition1 is false and condition2 is true…

#else// Emitted if neither is true…

#endif

Page 35: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#if defined

•  #if defined– Checks to see if a macro has been defined,

but doesn’t care about the value– A defined macro might expand to nothing, but

is still considered defined

Page 36: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Example#include <stdio.h>

#define DEBUG

int main()

{

#if defined DEBUG

printf(“Some debug output\n”); #endif

return 0;

}

Page 37: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

#undef•  Undefines a macro:

#include <stdio.h>

#define DEBUG int main() {

#if defined DEBUG

printf(“This is printed\n”);

#endif

#undef DEBUG #if defined DEBUG

printf(“This is not\n”);

#endif

return 0; }

Page 38: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Shortcuts for #if defined

•  #if defined → #ifdef•  #if !defined → #ifndef

Page 39: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Uses of #if directive•  Enable / disable code specific to a library, OS, CPU, etc.•  Turn on / off different features of program

–  Debugging: #ifdefDEBUG

printf(…)#endif–  More flexible debugging//easiertomodifyfunctionalityofPrintDebuglater#ifdefDEBUG#definePrintDebug(args…)fprintf(stderr,args)#else#definePrintDebug(args…)#endif

Page 40: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Using #if to #include!Header Only Once

•  Including same header twice can lead to compile errors–  Redefinition of the same struct type, etc..–  Easy to stumble into with multiple levels of nested headers

#ifndef _MYHEADER_H_ #define _MYHEADER_H_

…Declarations only to be included once

#endif

Page 41: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Command Line Defined Macros

•  –D: Defines variables from command line –  gcc –o test –DVERSION=5 test.c –  gcc –o test –DDEBUG test.c

•  Allows tailoring of source code to specific versions and features from command line

Page 42: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Pre-Defined MacrosMacro Meaning__FILE__ Current compiled file__LINE__ Current line number__DATE__ Current date__TIME__ Current time__STDC__ Defined if compiler supports ANSI C__GNUC__ Defined if compiler is GNU C compiler__VERSION__ Version of GNU C compiler__unix__ Defined if OS is a UNIX compliant system__i386__ Defined if CPU has x86 instruction set… Many other macros

Page 43: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Other Preprocessor Details•  # - places quotes around macro argument

–  #define CALL(f) { printf(#f); f(); } –  CALL(foo) → { printf(“foo”); foo(); }

•  ## - concatenates two things in macro–  #define CALL(f) f ## _debug () –  CALL(foo) → foo_debug()

•  #error: Emit error message and exit•  #warning: Emit warning message•  #pragma: Change behavior of compiler

Page 44: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

ErrorDirec4veExample#include<stdio.h>#ifndef__i386__#error"Needsx86architecture."#endifintmain(){//Somex86specificcodereturn0;}

>>gcc./error.c./error.c:3:2:error:#error"Needsx86architecture.>>gcc–m32./error.c

•  TestswhetherhardwareplaGormisi386(x86)anddisplayserror

•  Ini4allyfailsbecausedefaultcompila4ontargetisx86_64(and__i386__isnotdefined)

•  ‘-m32’op4onchangestargettox86,implicitlydefining__i386__

•  #warning: allowscompila4on but withwarningmessage

Page 45: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

Concatena4onExample#include<stdio.h>#ifdef__i386__#defineCALL(f)f##_x86()#else#defineCALL(f)f##_x86_64()#endifvoidfoo_x86(){printf(”x86binaryexecutable\n");}voidfoo_x86_64(){printf(”x86_64binaryexecutable\n");}intmain(){CALL(foo);return0;}

>>gcc./concat.c>>./a.outx86_64binaryexecutable>>gcc–m32./concat.c>>./a.outx86binaryexecutable

•  Callsdifferentfunc4onsdependingontargetarchitecture

•  When__i386__macroisundefined:CALL(foo)callsfoo_x86_64()

•  When__i386__macroisdefined:CALL(foo)callsfoo_x86()

•  ‘-m32’op4onimplicitlypasses‘-D__i386__’topreprocessor

Page 46: Practical C Issues - University of Pittsburghpeople.cs.pitt.edu/~wahn/teaching/cs449/slides/lecture13.pdf · Practical C Issues:! ... • Why break code into multiple source files?

PragmaExample#include<stdio.h>#pragmamessage"Compiling"__FILE__"

using"__VERSION__intmain(){return0;}

>>gcc./pragma.c./pragma.c:3:note:#pragmamessage:Compiling./pragma.cusing4.4.720120313(RedHat4.4.7-4)

•  Printsdiagnos4cmessageduringcompila4onoffile

•  Notetwopre-definedmacros:__FILE__and__VERSION__

•  Manymorepragmas–  Tocontrolstructpadding

(e.g.“#pragmapack(1)”)–  Tocontrolcodeop4miza4ons

(e.g.“#pragmaompparallel”)