450 likes | 564 Views
Lecture 3. Interfaces Pointers to Functions Memory bugs, File I/O Variables – the special kind. Interfaces. A definition of a set of functions that provide a coherent module (or library) Data structure (e.g., list, binary tree) User interface (e.g., drawing graphics)
E N D
Lecture 3 Interfaces Pointers to Functions Memory bugs, File I/O Variables – the special kind
Interfaces • A definition of a set of functions that provide a coherent module (or library) • Data structure (e.g., list, binary tree) • User interface (e.g., drawing graphics) • Communication (e.g., device driver)
Interface - modularity • Hide the details of implementing the module from its usage • Specification – “what” • Implementation – “how”
Interface – information hiding • Hide “private” information from outside • The “outside” program should not be able to use internal variables of the module • Crucial for modularity • Resource management • Define who controls allocation of memory
Example interface - StrStack • A module that allows to maintain a stack of strings • Operations: • Create new • Push string • Pop string • IsEmpty [See attached StrStack.h]
Implementation of StrStack Decision #1: data structure • Linked list • Array (static? dynamic?) • Linked list of arrays • … We choose linked list for simplicity
Implementation of StrStack Decision #2: Resource allocation • Duplicated strings on stack or keep pointer to original? • If duplicate, who is responsible for freeing them? We choose not to duplicate --- leave this choice to user of module
Implementation of StrStack • See StrStack.c
Using StrStack int main() { char *Line; StrStack *Stack = StrStackNew(); while( (Line = ReadLine()) != NULL ) StrStackPush( Stack, Line ); while( (Line = StrStackPop(Stack)) != NULL ) { printf("%s\n", Line ); free( Line ); } return 0; }
Interface Principles Hide implementation details • Hide data structures • Don’t provide access to data structures that might be changed in alternative implementation • A “visible” detail cannot be later changed without changing code using the interface!
Interface Principles Use small set of “primitive” actions • Provide to maximize functionality with minimal set of operations • Do not provide unneeded functions “just because you can”
Interface Principles Don’t reach behind the back • Do not use global variables or unexpected side effects • Do not assume specific order of operations by the user • Such assumptions suggest the set of primitives is wrong
Interface Principle Consistent Mechanisms • Do similar things in a similar way • strcpy(dest, source) • memcpy(dest, source)
Interface Principle Resource Management • Free resource at the same level it was allocated • Assumptions about resources
Pointers to Functions • C/C++ allow to have a pointer to a function int foo(int x) {…} main() { int (*func)(int); // funcis a pointer to a function func = &foo; func = foo; // same int x = (*func)(7); // same as x = foo(7) }
Example: Numerical Integrator double numericalIntegration( double a, double b, double (*func)(double), int k ) { double delta = (b - a)/k; double Sum = 0; for( double x = a+0.5*delta; x < b; x+= delta ) Sum += (*func)(x); return Sum*delta; } See NumericalIntergator.c
“Generic” interface • Pointers to functions provide a way to write code that receives functions as arguments • For example sorting – you can apply different comparison functions • increasing and decreasing order can be computed using the same algorithm code • Different data types can be sorted using the same algorithm code
Example: qsort Library procedure: qsort( void *base, size_t n, size_t size, int (*compare)(void const*, void const *) ); • base – start of an array • n – number of elements • size – size of each element • compare – comparison function
Using qsort int compareInt(void const *p, void const *q) { int a = *(int const*)p; int b = *(int const*)q; if( a < b ) return -1; return a > b; } … int array[10] = { … }; qsort( array, 10, sizeof(int), compareInt );
argv & argc • To pass command line arguments to our program we should use the following main declaration: main(int argc, char* argv[]) { ... • Compare to main(String[] args) in java. • Unlike java the first argument is the name of the program itself. • char** argv • char argv[][]
argv & argc: example • $ prog1 –u danny –p 1234argc = 5 argv[0] = “prog1” argv[1] = “-u” ... argv[4] = “1234”Always: argv[argc] = 0
File I/O • File I/O is mostly similar to stdin & stdout I/O. • Most I/O functions we encountered have a “file” counterpart which receives a FILE pointer (handle). • Examples:getchar(void) fgetc(FILE*) scanf(const char *,...) fscanf(FILE*, const char*,...)printf(const char *,...) fprintf(FILE*, const char*,...) • The standard streams (stdin, stdout, stderr) are also of FILE* type. See related man pages: fprintf, fscanf, etc.
File I/O example: mywc #include <stdio.h>#include <errno.h>#include <ctype.h>main(int argc, char* argv[]) { FILE* fp; int wc = 0, ch; if (argc != 2) { printf("Usage: mywc <filename>\n"); exit(1); } errno = 0; fp = fopen(argv[1], "r"); if (fp == NULL) { perror(“”); exit(1); }
File I/O example while (1) { while ((ch = fgetc(fp)) != EOF && isspace(ch)) ; if (ch == EOF) break; wc++; while ((ch = fgetc(fp)) != EOF && !isspace(ch)) ; if (ch == EOF) break; } fclose(fp); printf("There are %d words in %s\n", wc, argv[1]); return 0; } Related man pages: fopen, fclose
Static variables • Static variables in a function keep their value for the next call to the function • Allocated on the heap • void getUniqueID() { • static int id=0; • id++; • return id; • } • int main() { • int i = getUniqueID(); //i=1 • int j = getUniqueID(); //j=2 • }
Static variables, cont. • “static” variable on the global scope • Available only in the current module • “extern” variable • Defined outside the module file2.c extern int y; //y from file1.c extern int x;//x defined elsewhere int myFunc() { int y; //error … } file1.c int y; static int x; int myFunc() { int x;//error … }
C’s “const” • C’s “const” is a qualifier that can be applied to the declaration of any variable to specify its value will not be changed. const double e = 2.71828; const char msg[] =“Warning:”; msg[1] = ‘w’; // illegal !
1 2 3 1 2 3 C’s “const” • Do not confuse what the “const” declaration “protects” ! • A pointer to a const variable: int const * p = {1,2,3}; p[1] = 1; // illegal! *(p+1) = 1; // illegal! p = NULL; //legal • A const pointer to a variable: int* const const_p = {1,2,3}; const_p[1] = 0; // legal ! const_p = NULL; // illegal!
Pointer’s Syntax • Compare: (1)int * const p = {1,2,3}; (2)const int * p = {1,2,3}; (3)int const * p = {1,2,3}; • (2) and (3) are synonyms in C to a pointer to a const array.We encourage right to left reading of declarations, to achieve better readability and avoid errors.` • DO NOT TRY THIS AT HOME
Pointers and User Defined Types struct Complex { int img; int real; }; Complex comp2; Complex const comp1 = comp2; //ok,initialize using comp2 Complex comp3; comp1.img = 3; // illegal ! comp1 value is constant comp1=comp3; // illegal ! comp1 value is constant • All the members of a const variable are immutable !
Compare to Java’s “final” • All (methods as well as data ) are Class members. • “final” makes primitive types constants and references to objects constant. • The values inside the referred objects are not constant ! final int LIMIT = 10;int LIMIT = 11;// illegal ! final MyObject obj1 = MyObject(); MyObject obj2 = NULL;MyObject obj3 = MyObject();obj2 = obj1;//fine, both point now to the same objectobj1 = obj3; // illegal !obj1.setSomeValue(5); // legal ! * Because All are class members you would normally use them as class constants and declare them as “static final”
“Const” Usage • The const declaration can (and should !) be used in the definition of a function’s arguments, to indicate it would not change them: int strlen(const char []); • Why use ? (This is not a recommendation but a must) • Clearer code • Avoids errors • Part of the interfaces you define! • We will see more of “const” meaning and usage when we get to C++
Memory related bugs • Memory leaks. • Accessing random/freed memory addresses.
malloc_stats() • By including malloc.h you can use the malloc_stats() function which prints to the stderr information about the amount of used memory. • Example: . . . malloc_stats(); destroyDictionary(dict); malloc_stats(); . . .
malloc_stats() cntd. With memory leak: Arena 0: system bytes = 8140 in use bytes = 6860 . . . Arena 0: system bytes = 8140 in use bytes = 4084 . . . Without memory leak: Arena 0:system bytes = 8124 in use bytes = 6860. . . Arena 0:system bytes = 8124 in use bytes = 4 . . .
mtrace • Log all memory allocations to a file. • The file name is contained in the MALLOC_TRACE environment variable. • For example:$setenv MALLOC_TRACE ~/plab/ex1/trace • Analyze the file to find memory leaks using the mtrace utility. • The program must: • be compiled with –g flag • #include <mcheck.h>
mtrace example The program:#include <mcheck.h> int main() { mtrace(); // later we can call muntrace() . . . muntrace(); return 0; } The log file looks like this: = Start @ [0x80486fd] + 0x804a0e0 0x8 @ [0x804887d] + 0x804a0f0 0x8 @ [0x8048c7d] + 0x804a100 0x14 @ /lib/libc.so.6:(__strdup+0x29)[0x400d7a29] + 0x804a118 0x5 @ [0x8048c7d] + 0x804a128 0x14 @ /lib/libc.so.6:(__strdup+0x29)[0x400d7a29] + 0x804a140 0x3 @ [0x8048c7d] + 0x804a150 0x14. . .
mtrace example cntd. The result of analysis (mtrace ex1 $MALLOC_TRACE) Memory not freed: ----------------- Address Size Caller 0x0804a100 0x14 at /home/mush/plab/ex1/strBinTree.c:65 0x0804a128 0x14 at /home/mush/plab/ex1/strBinTree.c:65 0x0804a150 0x14 at /home/mush/plab/ex1/strBinTree.c:65 Another example: Memory not freed: ----------------- Address Size Caller 0x0804a118 0x5 at /lib/libc.so.6:(__strdup+0x29)[0x400d7a29] 0x0804a140 0x3 at /lib/libc.so.6:(__strdup+0x29)[0x400d7a29]
MALLOC_CHECK_ • By setting this environment variable to 0, 1, 2we can handle some bugs, most notably freeing twice the same memory. • Usually double free causes segmentation fault. • When MALLOC_CHECK_ is 0 freeing twice works. • When MALLOC_CHECK_ is 1 an error message is printed. • Example: free(): invalid pointer 0x80497b8! • When MALLOC_CHECK_ is 2 the program (gracefully) aborts.
ElectricFence • ElectricFence is a library which allows to catch accesses to memory that was already freed, as well as off-by-one errors. • It will cause the program to segfault in the above cases, which is usually better than continue running and have unpredictable errors later. Example: char* a = (char*)malloc(100*sizeof(char));. . . a[100] = 'c'; // ElectricFence will cause segfault
ElectricFence cntd. Example: Node* n1 = (Node*)malloc(sizeof(Node));. . . free(n1); . . . n1->x = 7; // ElectricFence will cause segfault • To use ElectricFence you should link your program with the efence library. • For example:g++ prog1.o list.o read.o -lefence
Commercial products • Purify • BoundsChecker • MS’s VisualStudio