compilation / pointers debugging 101. compilation in c/c++ hello.c preprocessor compiler stdio.h...

42
. Compilation / Pointers Debugging 101

Post on 20-Dec-2015

225 views

Category:

Documents


0 download

TRANSCRIPT

.

Compilation / PointersDebugging 101

Compilation in C/C++

hello.c

Preprocessor Compiler

stdio.h

tmpXQ.i(C code)

hello.o(object

file)

Preprocesser

A single-pass program that Include header files Expands macros Control conditional compilation Remove comments

#include directive

#include ”foo.h” Include the file “foo.h”, from current directory

#include <stdio.h> Include the file “stdio.h” from the standard

library directory (part of compiler installation)

Modules & Header files

Complex.c

struct Complex { double _real, _imag;};Complex addComplex(Complex, Complex);Complex subComplex(Complex, Complex);...

complex.h

#include <math.h>#include “complex.h”

// implementationComplex addComplex(Complex a, Complex b){ …

complex.c

#include “complex.h”int main(){ Complex c; …

MyProg.c

Header files

Header file contain Definition of data types Declarations of functions & constants

That are shared by multiple modules.

#include directive allows several modules to share the same set of definitions/declarations

#define directive

#define FOO 1

int x = FOO;

is equivalent to

int x = 1;

#define with arguments

#define square(x) x*x

b = square(a);

is the same as

b = a*a;

#define -- cautions

#define square(x) x*x

b = square(a+1);

c = square(a++);

Is it what we intended?

b = a+1*a+1;

// b = 2*a+1;

c = a++*a++;

// c = a*a; a+=2;

#define

#define directive should be used with caution

Alternative to macros: Constants

enum { FOO = 1; };

or

const int FOO = 1; Functions – inline functions (C++, later on)

#if directive

Allows to have conditional compilation#if defined(DEBUG)

// compiled only when DEBUG exists

printf(“X = %d\n”, X);

#endif

#if – header safety

Complex.h:struct Complex { …

MyStuff.h:#include “Complex.h”

Main.c#include “MyStuff.h”

#include “Complex.h”

Error:Complex.h:1: redefinition of `struct Complex'

#if – header safety

Complex.h (revised):#if !defined(COMPLEX_H)

#define COMPLEX_H

struct Complex {

#endif

Main.c:#include “MyStuff.h”

#include “Complex.h” // no error this time

Preprocessor

We can test what the preprocessor does

> gcc –E hello.c

will print the C code after running preprocess

assert.h#include <assert.h>

// Sqrt(x) - compute square root of x

// Assumption: x non-negative

double

Sqrt(double x )

{

assert( x >= 0 ); // aborts if x < 0

If the program violates the condition, thenassertion "x >= 0" failed: file "Sqrt.c", line 7

<exception>

The exception allows to catch the event in the debugger

assert.h

Important coding practice Declare implicit assumptions Sanity checks in code Check for violations during debugging/testing

Can we avoid overhead in production code?

assert.h#undef assert

// procedure that actually prints error message

void _assert(char* file, int line, char* test);

#ifdef NDEBUG

#define assert(e) ((void)0)

#else

#define assert(e) ((e) ? (void)0 :

__assert(__FILE__, __LINE__, #e))

#endif

Compilation

Takes input C-code and produces machine code (object file) gcc –c Main.c Main.c Main.o

The object file does not contain all external references It leaves names, such as “printf”,

“addComplex”, etc. as undefined references

Linking

Combines several object files into an executable file No unresolved references

Main

PreprocessorCompiler

Complex.c Complex.o

Main.c Main.o

Linker

libc.a

Link errors

The following errors appear only at link time Missing implementation> gcc -o Main Main.p

Main.o(.text+0x2c):Main.c: undefined reference to `foo'

Duplicate implementation> gcc -o Main Main.o foo.ofoo.o(.text+0x0):foo.c: multiple definition of `foo'

Main.o(.text+0x38):Main.c: first defined here

Memory Arrangement

Memory is arrange in a sequence of addressable units (usually bytes) sizeof( <Type> ) return the number of units

it takes to store a type. sizeof(char) = 1 sizeof(int) = 4 (on most of our machines)

Memory

int main()

{

char c;

int i,j;

double x;

c i j x

Arrays

Defines a block of consecutive cellsint main()

{

int i;

int a[4];

i a[0] a[1] a[2] a[3]

Arrays

C does not provide any run time checks int a[4]; a[-1] = 0;

a[4] = 0;

This will compile and run (no errors)

…but can lead to unpredictable results. It is the programmer’s responsibility to check

whether the index is out of bounds…

Arrays

C does not provide array operations int a[4];

int b[4];

… a = b; // illegal

if( a == b ) // illegal

Array Initialization int arr[3] = {3, 4, 5}; // Good

int arr[] = {3, 4, 5}; // Good - The same

int arr[4] = {3, 4, 5}; // Good - The last is 0

int arr[2] = {3, 4, 5}; // Bad

int arr[2][3] = {{2,5,7},{4,6,7}}; // Good

int arr[2][3] = {2,5,7,4,6,7}; // Good - The same

int arr[3][2] = {{2,5,7},{4,6,7}}; // Bad

int arr[3];

arr = {2,5,7}; // Bad - array assignment only in initialization

Pointersint main()

{

int i,j;

int *x; // x points to an integer

i = 1;

x = &i;

j = *x;

x = &j;

(*x) = 3;i j x

1

Pointersint main()

{

int i,j;

int *x; // x points to an integer

i = 1;

x = &i;

j = *x;

x = &j;

(*x) = 3;i j x

10x0100

0x0100

Pointersint main()

{

int i,j;

int *x; // x points to an integer

i = 1;

x = &i;

j = *x;

x = &j;

(*x) = 3;i j x

10x0100

0x01001

Pointersint main()

{

int i,j;

int *x; // x points to an integer

i = 1;

x = &i;

j = *x;

x = &j;

(*x) = 3;i j x

10x0100

0x01041

Pointersint main()

{

int i,j;

int *x; // x points to an integer

i = 1;

x = &i;

j = *x;

x = &j;

(*x) = 3;i j x

10x0100

0x01043

Pointers

Declaration

<type> *p;

p points to objects of type <type> Pointer value

*p = x;

y = *p;

*p refers to the object p points to value pointer

&x - the pointer to x

Example – the swap functionDoes nothing Works

void swap(int a, int b){ int temp = a; a = b; b = temp;}….int main(){ int x, y; x = 3; y = 7; swap(x, y); // now x==3, y==7….

void swap(int *pa, int *pb){ int temp = *pa; *pa = *pb; *pb = temp;}….int main(){ int x, y; x = 3; y = 7; swap(&x, &y); // x == 7, y == 3…

Pointers & Arrays

int *p;

int a[4];

p = &a[0];

*(p+1) = 1; // assignment to a[1]!

p a[1] a[2] a[3]a[0]

Pointers & arrays

Arrays are essentially constant pointers int *p;

int a[4];

p = a; // same as p = &a[0]

p[1] = 102; // same as *(p+1)=102;

*(a+1) = 102; // same

p++; // p == a+1 == &a[1]

a = p; // illegal

a++; // illegal

Pointers & Arrays

int foo( int *p );

andint foo( int a[] );

Are declaring the same interface In both cases, a pointer to int is being passed

to the function foo

Pointer Arithmetic

int a[4]; int *p = a;

char *q = (char *)a; // Explicit cast

// p and q point to the same location

p++; // increment p by 1 int (4 bytes)

q++; // increment q by 1 char (1 byte)

a[1] a[2] a[3]a[0]

q p

Pointer arithmeticint FindFirstNonZero( int a[], int n )

{

int *p;

for( p = a; p < a+n && (*p) == 0; p++ )

;

return p-a;

}

Same as int FindFirstNonZero( int a[], int n )

{

int i;

for( i = 0; i < n && a[i] == 0; i++ )

;

return i;

}

void *

void *p defines a pointer to undetermined type

int j;

int *p = &j;

void* q = p; // no cast needed

p = (int*)q ; // cast is needed

NULL pointer

Special value: uninitialized pointer

int *p = NULL;

if( p != NULL )

{

}

NULL pointer

int *p = NULL;

*p = 1;

Will compile…

… but will lead to runtime error

Debugging 101

1. “Define” the bug --- reproduce it

2. Use debugger

3. Don’t panic --- think!

4. Divide & Conquer