By the time you have completed this extra lab, you should be able to:
Even if you haven't yet run into a segmentation fault in your C programming in CS16, eventually you will—either in CS16, CS24, CS32, or CS48, or a later course.
It is ok to put off doing this extra lab until that happens.
Segmentation faults are a very common problem in C and C++ programming, so the sooner you learn about...
...the better off you will be!
You may do this individually, or with your pair partner—but in my opinion, together is better...
It's up to you—but I think you'll learn more if you work together with a partner on this. Each of you can help each other understand what is going on, and each of you may notice different things.
There's a lot of reading in this supplemental lab
Sorry about that—it's just that there is a lot to explain here. I realize that may make the whole pair thing more awkward.
You may want to do the reading first—i.e. read over the whole lab before you start, then do all the hands on exercises.
The reading probably makes the most sense though when it is connected directly with the experience of trying these commands for yourself.
So another option is to go ahead and log in at two computers, near one another. Work the hands-on parts together pair-programming style (driver/navigator), but when you are reading, go back to your separate machines so you can each read at your own pace.
The files you need are available here:
http://www.cs.ucsb.edu/~pconrad/cs16/10W/labs/el01/files
And here on the CSIL computers:
~pconrad/public_html/cs16/10W/labs/el01/files/*
Copy those into file into your ~/cs16/el01 directory—see the early labs in this course if you've forgotten how.
The program sumArrayCorrect.c shows an example of a correct program that uses test-driven development to calculate the sum of an array of integers. It illustrates several points that may be helpful to you in learning C.
Run it once to see what it does. No suprises here—it looks just like every other test-driven development program we've done so far.
But look more deeply into the code, and notice the following things:
Once you understand this program, try the next one—because it is a broken version of this same code.
The program sumArrayWrong.c contains—on purpose—an error. It is not a syntax error—the program compiles just fine.
In a sense, it isn't a logic error either—it isn't that the programmer "thought about the program in the wrong way".
Rather, it is, in a real sense, a "typo"—the programmer typed a character that looks like the one she/he intended, but ended up with a different character instead—one that, unlucky for him/her, happens to still be a program that will compile and run—it just won't work.
Once you've spotted the error move on to the next step. If you can't find it after a while, just keep going with Step 4—you'll find it eventually later in this lab.
Without fixing the error (if you found it), compile and run sumArrayWrong.c
If you do, you'll probably see a result like this. I've used a "green highlighter" to make the key result stand out:
-bash-3.2$ make sumArrayWrong
cc sumArrayWrong.c -o sumArrayWrong
-bash-3.2$ ./sumArrayWrong
Segmentation fault
-bash-3.2$
These words—Segmentation fault
—are often associated with fear and dread among C and C++ programmers. But this need not be the case.
With the right set of tools at your disposal, a segmentation fault is no more difficult than any other kind of error to debug. In fact, it can be a very convenient kind of error—because it causes your program to immediately halt. We can use a couple of tools to go in, like doctors using an X-Ray or MRI, and examine exactly what is going on in the program at that moment.
The tools we need are:
In the next step, we'll use those tools to find which line of code resulted in the segmentation fault.
Let's apply these tools to sumArrayWrong.c—try these commands for yourself:
-bash-3.2$ gcc -g sumArrayWrong.c -o sumArrayWrong
-bash-3.2$ gdb sumArrayWrong
GNU gdb Fedora (6.8-32.fc10)
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb)
You are now at a special prompt called the gdb prompt. The letters gdb stand for "GNU debugger"—this program is sometimes just called "the debugger".
There are many commands we can type here—and we'll learn about many of them over the next few weeks. But the only three we'll need for this lab are:
Let's start with the run command:
(gdb) run Starting program: /cs/faculty/pconrad/cs16/el01/sumArrayWrong Program received signal SIGSEGV, Segmentation fault. 0x08048413 in sumArray (a=0xbffff5e4, n=3) at sumArrayWrong.c:27 27 sum += a[i]; (gdb)
Well, that is informative! We see exactly what line of code it was that cause the segmentation fault—namely the line:
sum += a[i];
In a moment we'll come back to why that line of code caused a problem. But first, lets take a short break to read about what a segmentation fault is:
It may help at this point to know what a segmentation fault is. This is an oversimplified explanation, but it will do for our purposes:
A segmentation fault occurs when:
Here's a slightly more detailed explanation:
The word fault is used here in the same sense that is used in the game of tennis—a fault is just a fancy word for mistake, or error, or "violation of the rules".
We often call these seg faults for short.
Seg faults don't happen if your code is written properly. So what causes them? The two most common problems are:
(A side note: If youv'e begun to penetrate the deep connection between "array indexing" and "pointers" in C—you'll understand that these "two" kinds of segfaults are actually, at a deep level, really just the same thing. If you don't see that yet, don't worry—you'll see that by the end of the course, I hope.)
Let's return now to our sumArrayWrong.c program, where we'll see that the segmentation fault is in fact caused by an array indexing error..
When we left off to read a bit about seg faults, we were here (this is the result of step 5):
We had used the run command:
(gdb) run Starting program: /cs/faculty/pconrad/cs16/el01/sumArrayWrong Program received signal SIGSEGV, Segmentation fault. 0x08048413 in sumArray (a=0xbffff5e4, n=3) at sumArrayWrong.c:27 27 sum += a[i]; (gdb)
And we had found that the following line was the one being executed—executed in the sense of being "done", not in sense of being killed—when the seg fault occured.
sum += a[i];
Because we know that seg faults usually happen because of array indexing, or pointer deferencing, we suspect that the problem may be that either i has the wrong value, or a points to the wrong place. To check these theories, we can use the print command. The print command lets us look at the values of various variables. Let's check the values of a[0] and a[1] to see if they seem reasonable:
(gdb) print a[0]It appears that a[0] is 4, and a[1] is 8. That sounds right, because in the main program of sumArrayWrong.c, we see this:
$1 = 4
(gdb) print a[1]
$2 = 8
(gdb)
int main() { int failures = 0; // this counts the number of failures int a[] = {4,8,10}; // sum 22 int b[] = {10,20,30,40}; // sum 100 int c[] = {-1,1,2,-2,3,-3}; // sum 0 // We use += to add the number of failures each time...
failures += checkExpectInt("1: sumArray(a,3)",sumArray(a,3),22); ...
The array a that contains {4,8,10} is the one that we are passing into the function sumArray as the formal parameter a (this happens to be the name of the array in the main program also).
So, that seems to be working—the values in a[0] and a[1] are what we expect. But if we examine the variable i, we see something truly strange:
(gdb) print iHow can this be? The array a only has three elements—n is 3, right? So shouldn't i only go from 0 up to 2?
$4 = 647
(gdb)
So, we look at the for loop that gives i its value, and we see the culprit, here highlighted in green:
for (i=0;1<n;i++) // @@@ BUG!!! this line contains an error
Clearly, that is not what we intended to write. The condition (1<n) is always true, so we have an infinite loop, where i keeps getting incremented "forever"—we'll not exactly forever, because when it hits the value 647, we have strayed outside the bounds of the segments we are allowed to read—and this leads to a segmentation fault.
It should be noted that we strayed outside the bounds of the array much earlier—but we must have still been in segments of memory where we had "read access"—so we didn't get a seg fault then. This illustrates a basic property of C and C++ which is both a weakness and a strength:
Back to our code: what we intended was (i < n), so that the index variable i stays within the bounds of the array, i.e the values 0 through n-1:
for (i=0;i<n;i++)
What may not be obvious, but I hope you can begin to see is this: the segmentation fault is our friend. Here's why:
Use the quit command. If the program is still running, you may need to say "y" at the prompt:
(gdb) quit
The program is running. Exit anyway? (y or n) y
-bash-3.2$
We've covered a lot of ground in this assignment. But the information is sort of scattered all over the web page. So to finish up, here's a sample session with using our segfault-busting tools—all in one place. You can use this as a roadmap when you have your own segfaults to deal with. Good luck!
You encounter a program with a segmentation fault
-bash-3.2$ make sumArrayWrong
cc sumArrayWrong.c -o sumArrayWrong
-bash-3.2$ ./sumArrayWrong
Segmentation fault
-bash-3.2$
You recompile the program with gcc -g
-bash-3.2$ gcc -g sumArrayWrong.c -o sumArrayWrong
-bash-3.2$
You run gdb (the GNU debugger on your program)
-bash-3.2$ gdb sumArrayWrong
GNU gdb Fedora (6.8-32.fc10)
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb)
You type run at the gdb prompt to run your program, and see which line caused the segfault
(gdb) run Starting program: /cs/faculty/pconrad/cs16/el01/sumArrayWrong Program received signal SIGSEGV, Segmentation fault. 0x08048413 in sumArray (a=0xbffff5e4, n=3) at sumArrayWrong.c:27 27 sum += a[i]; (gdb)
You use the print command to look at variable values and other expressions. You can look at the line of code the caused the seg fault (in this case sum += a[i]
) for ideas of what variables and expressions to examine.
The values you get back can give you some insight into what might be wrong. In this case, the value of i (647) is especially suspicious:
(gdb) print a
$1 = (int *) 0xbffff5e4
(gdb) print a[0]
$2 = 4
(gdb) print a[1]
$3 = 8
(gdb) print a[i]
Cannot access memory at address 0xc0000000
(gdb) print i
$4 = 647
(gdb) print sum
$5 = 451829096
(gdb)
Based on what you see, you form a hypothesis about what is wrong, look for the problem in your code, and then fix your code. (It can help to have another window open—in that window, you are using the editor—e.g. emacs or vi—to look through your program source code.)
When you've found the changes you want to make to the code, you quit gdb, and start over again—with compiling and running your code.
(gdb) quit
The program is running. Exit anyway? (y or n) y
-bash-3.2$
For further exploration:
In the same directory with sumArrayCorrect.c and sumArrayWrong.c, you'll also find two other programs:
You may find it helpful to look at those two programs and run them as well—repeating all the steps from this lab on those two programs. Because these programs print out the values they are examining, it may be a little bit easier to see what is going on—this may give you more insight into the conditions that create a segmentation fault.
If you are a relatively new programmer, the printArrayCorrect.c program may also be another useful example in at least three ways—so it is worth looking at:
Copyright 2010, Phillip T. Conrad, CS Dept, UC Santa Barbara. Permission to copy for non-commercial, non-profit, educational purposes granted, provided appropriate credit is given; all other rights reserved.