10.17.05 CISC181 Friday Last Monday 10/14 We talked about const char * const name And about const char * const a for arrays in general... char *arrayname; // no protection if (arrayname = NULL) // this would be a logic error, not compiler error ... if (arrayname[0] = 'x') // logic error, not compiler error ... const char * arrayname; if (arrayname = NULL) // this would be a logic error, not compiler error ... if (arrayname[0] = 'x') // compiler error ... char * const arrayname; // constant pointer to char if (arrayname = NULL) // compiler error ... if (arrayname[0] = 'x') // this would be a logic error, not compiler error ... const char * const arrayname; // constant pointer to char constant if (arrayname = NULL) // compiler error ... if (arrayname[0] = 'x') // compiler error ... See program demoConstCharStuff.cc for demonstration (the prohibited lines don't compile We started demoPracticalUseOfConstCharStuff.cc but we didn't finish it... the current version has a seg fault which we will debug now (1) What to do when you get a segmentation fault!!!! To debug a "seg fault" or "bus error" (For purposes of these instructions, consider a "bus error" to be the same as a seg fault. See description at the end to find out the difference.) (1) First, turn on the generation of core files, by doing the following. If running csh or tcsh, use: unlimit core If running bash, use ulimit -c 0 (2) Now, recompile all your code with the -g flag turned on. For a simple C program, you'd do: cc -g foo.c and the resulting executable would be a.out. For a simple C++ program, you might do either of the following: CC -g myprog.cpp or g++ -g myprog.cpp Now your a.out file will have "debugging info" in it. Or, if you are compiling with the -o option, use: CC -g -o myprog myprog.cpp OR CC -g myprog.cpp -o myprog (you can use g++ on either of these instead of CC). If you have a complicated project, you have to put the -g flag on every line that is compiling, for example: g++ -g -c myClass.cpp g++ -g -c myMain.cpp g++ -g -o foo myClass.o myMain.o Notice how the -g flag is always turned on. (I think that actually, it might not matter on the link step, only on the compiling steps.. but it doesn't hurt to have it on the whole time.) Again, it doesn't matter if you use g++ or CC as long as you are consistent. If you have a Makefile, this process of putting -g on every compile can be made much easier by just doing: CCC = CC -g or CCC = g++ -g then doing "make clean; make all" (3) Now, run the code that generates the seg fault. You should see "Segmentation fault (core dumped)". If you don't see (core dumped), make sure that you did step 1. If you did step 1 and you STILL don't see (core dumped), make sure you are not over your quota (use quota -v). If you did both of those and you STILL STILL don't see (core dumped), then make sure your current directory is writable. If none of that works, see your professor or TA for help, and don't bother with the rest of these instructions. (4) You should now have a "core" file in your directory. If you used "gcc" or "g++" to compile, now you need to use "gdb" as the "debugger" in the instructions that follow. Otherwise, I'll assume you used "cc" or "CC", in which case you should use "dbx" as the debugger. Unfortunately, you might have to type the "full path name" for gdb. That path name is "/opt/sfw/bin/gdb". Assuming your executable program name is "foo", type the following: dbx foo core or, if using gcc: /opt/sfw/bin/gdb foo core You should now see a bunch of stuff on your screen. You might need to page down through a bunch of nonsense to get to the good stuff. But the good stuff will end with something like the following, which will tell you EXACTLY where your seg fault occured: After you've seen what line had the seg fault, use "quit" to exit the debugger. Bus error vs. Segmentation Fault What is a "bus error"? A Bus Error occurs when you try to dereference a pointer to a memory location that is not aligned correctly. For example, on a 32-bit machine, such as strauss, integers must be aligned on addresses that are multiples of 4. If you dereference a pointer that is not evenly divisble by 4, you get a bus error. The cause of a bus error is usually an attempt to dereference a pointer that has not been initialized properly, and therefore contains arbitrary bits (junk) that are not a multiple of 4. What is a "segmentation fault"? The operating system and the hardware work together to divide up all memory into "segments". Some segments contain your variables. You have read/write access to these segments. Some segments contain constants, or executable code. You typically have read-only access to these segments. Some segments are forbidden for a normal user to access. These might contain operating system data, or data allocated to other users. If you dereference a pointer, and it points (by accident, typically) to a segment that you don't have any access to, you get a segfault. Or, if you try to write into a segment where you have read only access, that will also produce a segfault. It may be the case that the hardware checks for the alignment of a pointer BEFORE it checks for the segment type. So, a pointer outside your valid memory segments will typically produce a bus error if it is not properly aligned, rather than a seg fault (e.g. a pointer to int that is not a multiple of 4.) (2) back to demoPracticalUseOfConstCharStuff.cc We saw that originally we had if (label = NULL) when we should have had: if (label == NULL) Before, we had "char * label" as the type of our formal parameter label... When we changed it to "const char * const label" the compiler flagged the "if (label = NULL)" as an error, and we were able to find the cause of the seg fault. Take away lesson: *********************************** * Everything that can be const, * * should be const. * *********************************** (3) Arrays: finding the min of an array (a) We modified findMax to do findMin (b) We modified findMin to find the location of the min (c) We talked about two unsuccessful ways of doing a swap: void swap(int a, int b) { a = b; b = a; return; } void swap(int a, int b) { int temp; temp = a; a = b; b = temp; return; } We talked about why both are unsuccessful, then very briefly mentioned that this technique WILL be successful: void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; return; } NEXT TIME: More on reference parameters, aliases, and the various uses of the & in various contexts.