Real C C Traps and Pitfalls Partially based
Real C C Traps and Pitfalls Partially based on: C Traps & Pitfalls, A. Koenig Various resources from the WWW
Overview ¡ C traps and pitfalls l ¡ A bit about OOP and OOD l ¡ And some programming tips With some related Buzz-Words Introduction to project development lifecycle
Greedy lexer Take a look at the following function: double devide_by_pointer(double x, double *p) { double ret; ret = x/*p; /* p points at the divisor */ } greedy 1. c
Greedy lexer The lexer is a part of the compiler that breaks the code text into tokens ¡ It greedy - bites off the biggest possible piece of text ¡ Whereas == = = is a single token are two tokens But not a---b is the same as a -- - b a - -- b
Greedy lexer ¡ In the early days =+ was the what now is += ¡ Why did they change it ? a=-1; This is actually: a = a – 1; But we meant: a = – 1;
Integer constants ¡ C allows very convenient ways to set integer constants /* My error codes */ #define EVERYTHING_OK #define NON_RECOVERABLE_ERROR #define ILLEGAL_PARAMETER_A … #define NOT_CONNECTED … 0010 is octal => 8 decimal 0000 9999 0001 0010
Strings and Characters ¡ Single quotes ’a’ are very different from double quotes ”a” Writing: char c = ‘a’ char c = 0141 is just the same as or 97 Writing: char *hello = “hello” is a pointer to the initial character to an array Nameless array: ‘h’, ’e’, ’l’, ’o’
Strings and Characters ¡ Consider this case Whats the difference between: printf(‘n’); printf(“n”); and
Operator precedence ¡ Testing bits … #define ERROR_BIT 0 x 04 … { … if (flags & ERROR_BIT) { /* handle error */ } }
Operator precedence ¡ Testing bits … #define ERROR_BIT 0 x 04 … { … if (flags & ERROR_BIT != 0) { /* handle error */ } }
Operator precedence ¡ Testing bits … #define ERROR_BIT 0 x 04 … { … if (flags & (ERROR_BIT != 0)) { /* handle error */ } BUG } if ((flags & ERROR_BIT) != 0) { OK
Operator precedence 2 nd ¡ Suppose we have to integer variables hi and low l l Each are between [0. . 15] (4 bits) We want to build an 8 bit number out of both of them i = hi*16+lo; Works but uses multiplication i = hi << 4 + low; Bug: i = hi << (4 + low) Fix is either: i = (hi << 4) + low i = hi << 4 | low;
Operator precedence 2 nd ¡ Use parenthesis i = (hi << 4) | low; if ((income > 4000) && (age > 18)) Etc.
Watch Out for Semicolons int calc_max(int *x, int size) { for … { if (x[i] > big); big = x[i]; } } The compiler will happily digest the semicolon to: if (x[i] > big) {} big = x[i];
Dangling else if (x == 0) if (y == 0) error(); else { z = x + y; f(z); } ¡ ¡ The meaning was two cases: x = 0 and x != 0 However else is associated with the closest unmatched if. if (x == 0) if (y == 0) error(); else { z = x + y; f(z); }
Dangling else ¡ Always use curly braces ‘{}’ with if-else statements if (x == 0) { if (y == 0) error(); } else { z = x + y; f(z); }
Pointers vs. Arrays ¡ ¡ C has only one-dimensional arrays Only two things can be done to an array l l ¡ Determine its size Obtain a pointer to element 0 Everything else is done using pointers ! Array declaration: int a[3]; struct { double v[3]; double length; } b[17]; int calendar[12][31]; sizeof(calendar) is 372 (12*31) int’s
Pointers vs. Arrays ¡ Pointers may happen to point to an element of an array l l We can use that pointer to obtain a pointer to the next/previous element of that array Add/Subtract 1 for that pointer int a[10]; … int *p; p = a; p++; p--; /* take the pointer to a[0] */ /* next element */ /* previous element */
Pointers vs. Arrays ¡ Adding an integer to a pointer is different from adding that integer to the bit representation of that pointer int *p; p = p + 1; /* this advances p’s value (pointedaddress) by sizeof(int) which is usually not 1 */
Pointers vs. Arrays ¡ The name of the array is the same as a pointer to the array in every context but one int a[3]; int *p; p = a; *a = 5; *(a+1) = 5; /* p points to a[0] */ /* sets a[0] to 5 */ /* sets a[1] to 5 */ The only difference is in sizeof: sizeof(a) /* returns the size of the entire array not just a[0] */
Pointers vs. Arrays int a[3]; int i; a+i i+a &a[i] /* is the same as */
Pointers are not Arrays ¡ Suppose we want to concatenate string s and string t into a single string char *r; strcpy(r, s); strcat(r, t); Doesn’t work cause r doesn’t point anywhere. Lets make r an array – now it points to 100 chars char r[100]; strcpy(r, s); strcat(r, t); This works as long as the strings pointed to by s and t aren’t too big
Pointers are not Arrays We would like to write something like: char r[strlen(s)+strlen(t)]; strcpy(r, s); strcat(r, t); However C requires us to state the size of the array as constant. char *r; r = malloc(strlen(s)+strlen(t)); strcpy(r, s); strcat(r, t); This fails for three reasons: 1. Malloc might fail to allocate the required memory 2. It is important to free the memory allocated 3. We did not allocate enough memory
Pointers are not Arrays The correct code will be: char *r; r = malloc(strlen(s)+strlen(t)+1); if (!r) { /* print some error and exit */ exit(1); } strcpy(r, s); strcat(r, t); /* later */ free(r); r = NULL; /* Try to reset free’d pointers to NULL */
Copying pointers ¡ A common C pitfall is to confuse a pointer with the data to which it points char *p, *q; p = strdup(“xyz”); … ‘x’ ‘y’ ‘A’ ‘z’ ‘ ’ q = p; q[1] = ‘A’; /* p would point to “x. Az” too */
Using CORE dumps ¡ On Unix (including Linux) a process is usually killed if it access memory incorrectly l ¡ This usually happens due to pointers going mad and bad Before the process is killed important information on why it was killed is saved in a core file l This information can be used to pinpoint what went wrong
Using CORE dumps Lets look at this faulty program (test. c) that tries to access a NULL pointer #include <stdlib. h> int main() { int* i = NULL; *i = 5; } /* This will cause a core dump in Unix/Linux */ return 0; Running this program yields the expected error message: Segmentation fault (core dumped)
Using CORE dumps To use the core dump the program must be compiled with debug information. • This is done by adding –g flag to the gcc compilation line For example: gcc –g test. c –o my. Test (Make sure that your core file was generated while running the program compiled with debug information before continuing to the next step - you may need to run the program again after adding flag) Now run: gdb <program executable> core and write ‘where’ at the prompt – you should get a stack trace and entry #0 is the source code line were the error had occurred. Here is the command line and output for our previous faulty program nova~> gdb my. Test core GNU gdb Red Hat Linux (5. 2 -2) Copyright 2002 Free Software Foundation, Inc. Core was generated by `. /my. Test'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/i 686/libc. so. 6. . . done. Loaded symbols for /lib/i 686/libc. so. 6 Reading symbols from /lib/ld-linux. so. 2. . . done. Loaded symbols for /lib/ld-linux. so. 2 #0 0 x 080483 d 0 in main () at test. c: 6 6 *i = 5; (gdb) where #0 0 x 080483 d 0 in main () at test. c: 6 #1 0 x 42017589 in __libc_start_main () from /lib/i 686/libc. so. 6 (gdb)
Counting and Asymmetric Bounds int i, a[10]; for (i=0; i <= 9; i++) {. . . } While looping over arrays Always use asymmetric bounds: < instead of <= for (i=0; i < 10; i++) {. . . }
Macros ¡ Macros are not functions l Even when written with all the parentheses #define MAX(a, b) (((a)>(b)) ? (a) : (b)) int a[4]; int biggest; biggest = x[0]; i = 1; while (i < 4) biggest = MAX(biggest, x[i++]); This code would have worked fine if MAX was a function
Macros Let’s expand the macro: #define MAX(a, b) (((a)>(b)) ? (a) : (b)) int a[4]; int biggest; biggest = x[0]; i = 1; while (i < 4) biggest = (biggest > x[i++]) ? biggest : x[i++];
Macros ¡ Macros are not statements l Lets try to write the assert macro assert(x > y); /* should print info only if x <= y */ #define assert(e) if (!(e)) assert_error(__FILE__, __LINE__) This macro is not safe: if ((x > 0) && (y > 0)) assert(x > y); else assert(y > x);
Macros We can try and add braces: assert(x > y); /* should print info only if x <= y */ #define assert(e) { if (!e) assert_error(__FILE__, __LINE__) } This will solve the dangling else problem but will cause a problem with the semicolon: if ((x > 0) && (y > 0)) { if (!(x > y)) assert_error(__FILE__, __LINE__) }; else { if (!(y > x)) assert_error(__FILE__, __LINE__) }; The correct solution is: #define assert(e) ((void)((e) || assert_error(__FILE__, __LINE__)))
OOD and OOP
Some of C Strengths ¡ It is close to machine language l ¡ Provides efficient code Highly portable l Compile the code on different platforms Many newer languages are similar in syntax ¡ Both Linux and most Unix operation systems are written in C ¡ Even large parts of Windows are in C ¡
Some of Its Weaknesses Surprising but Code efficiency is usually not so important ¡ More important are ¡ l l l ¡ Shorter development times Higher maintainability and extensibility All translates to cutting development costs Developing and maintaining large projects in C is difficult
Object Oriented Programming (OOP) ¡ C, Pascal and a bunch of other languages are procedural languages l ¡ Procedural languages tend to produce spaghetti code Objects describe the real world better l l l Real world objects share two characteristics: state and behavior Bicycles have state: current gear, number of gears etc. Bicycles have behavior: braking, accelerating, changing gears etc.
Object Oriented Programming ¡ In software objects are a bundle of state and behavior l l ¡ State is stored in variables Behavior is implemented with methods The benefit of Object Oriented Programming (OOP) is that you can more easily represent real world objects using software objects
Object Oriented Programming ¡ Modularity l l ¡ Objects source-code is maintained independently of other objects An object can be passed around in the system and it will still work Information hiding l l An object has a public interface for others to communicate with it Yet, it can maintain private information that is not essential to using it ¡ Black-Box: You don’t need to understand the gear mechanism of your bikes to use it
Object Oriented Programming ¡ Encapsulation l ¡ Packaging objects variables within a protective custody of methods However, sometimes it is useful expose some of the inner workings of an objects
Object Oriented Programming (OOP) ¡ Here’s a C++ example of our bicycles class Bicycle { public: // Constructor for a bicycle object Bicycle(int num. Gears, t. Gears curr. Gear = GEAR 1); // Break to a stop void break(); // Slowdown void slowdown(double target. Speed); double get. Current. Speed(); t. Gear get. Current. Gear(); private: int m_num. Gears; t. Gears m_curr. Gear; …
Object Oriented Programming ¡ A single object by itself is not very useful l l ¡ An object appears as a component of a larger program with many other objects The interaction between the objects achieves the complex, high-order functionality. Objects interact by invoking each others methods l You object might interact with Your. Bicycle object by invoking its change. Gears method.
A Few Words About Design ¡ Design is something you do when your brain can’t hold the entire project l ¡ Managing complexity by splitting big projects/problems into manageable chunks (that do fit your brain) Design methods provide the framework and language that allows to store and communicate decisions
Object Oriented Design (OOD) OOD is modeling a problem using objects. ¡ A key tool in managing complexity is abstraction ¡ l l Elimination of the irrelevant Example: You can teach someone to drive any car by teaching the essentials (steering wheel, ignition) and ignoring the details (particular engine, how fuel is pumped)
Object Oriented Design (OOD) ¡ Real problems usually have levels of abstraction l l l Car users can see the car at a high level of abstraction However mechanics need to work at a lower level of abstraction (higher detail) Yet, the mechanics too don’t see all the details ¡ For example, they may test the battery without caring that’s inside there’s a complex chemical reaction going on
Object Oriented Design (OOD) ¡ In software it abstraction looks something like this:
Object Oriented Design (OOD) ¡ OOD is the process of modeling a problem using objects l l ¡ Define the interaction between objects And between levels of abstraction To aid the process – there are ready made solutions for various types of problems called “Design Patterns”
Buzz Words Overview ¡ Java l l ¡ Write once run anywhere Virtual machine technology Garbage collection and easy memory management JIT (Just In Time compilation) . NET framework l l l Write once use anywhere Common virtual machine to different languages C# is the leading language
Buzz Words Overview ¡ UML l l ¡ Desing Patterns l ¡ Modeling language for OOD Very popular Set of ready made design solutions for reoccurring problems RMI, CORBA and Remoting l Remote method invocation – calling a method of an object that resides on a remote machine
Project Development Lifecycle ¡ ¡ Project times is measured in years Many models for project development l ¡ Contract model, extreme programming etc. . In the contract model: l l l Step 1: Gather requirements in the clients language Step 2: Translate into software engineering requirements (SRD) Step 2: Design documents (SDD) – if big projects there may be many levels of abstractions Step 3: Coding (usually no more the 20 -30% of the projects time) Step 4: Builds and testing Step 5: Deployments
The End
- Slides: 51