As you may recall from 1027, debuggers are programs that allow you to execute programs line by line, letting you check for bugs much easier
Unix has these as well, including gbd, a text based debugger that can:
The most common way to start is with a program file
% gbd program
You can also include either a core file or a process ID as another argument, if you want to debug a running process
% gbd [program] [core|procID]
You can also use your compiler for debugging with the -g switch, which is better most of the time
% gcc -o name -g file1.c file2.c ...
You should also use the -wall option for all the warnings
The following is a list of common gbd commands:
b(reak) [file:]line_num Set a breakpoint at line_num (in file).
r(un) [arglist] Start program (with arglist, if specified).
bt or where Backtrace: display the program stack; especially useful to find where your program crashed or dumped core.
print expr Display the value of an expression.
display expr Display the value of an expression each time the program stops.
x address_expr Examine the memory at address_expr.
c Continue running your program (after stopping, e.g. at a breakpoint).
n(ext) Execute next program line (after stopping); step over any function calls in the line.
s(tep) Execute next program line (after stopping); step into any function calls in the line.
l(ist) [file:]line_num print the source code at line_num (in file).
l(ist) [file:]function print the source code of function (in file).
f(rame) [num] Select and print a stack frame.
help [name] Show information about GDB command name, or general information about using GDB.
For the x command, this is the general form
% gbd x/FMT ADDRESS
ADDRESS is an expression of a memory address, while FMT is a three letter code
For example, suppose i is an integer
Structures are kind of like objects in Java, with a constructor looking like this
struct person {
char first[32];
char last[32];
int year;
double ppg;
}; //don't forget the ; !!!
We can then create a person variable
int i;
struct person teacher;
In our memory map, the structure will take up space for however many elements are in the parameters, with the first element being the label of the structure
We can access components much like we can in Java
teacher.year = 2005;
We can also assign structure of the same type to other structures, which creates a byte-by-byte copy
struct person mailman;
mailman = teacher;
We can also include structures into other structure definitions, creating nested structures
struct name {
char first[30];
char last[30];
};
struct person {
int age;
float ppg;
struct name title;
};
Structures can also be made into arrays or pointers, behaving just like other variables
struct fraction {
int wP;
int fP;
};
struct fraction f[3], *g;
f[0].wP= 3;
f[0].fP= 7;
g = &(f[0]);
For pointer arithmetic and array, each structure is treated as one variable, which gives you the following
g++;
/* g now
contains
400 + 8
408 */
We also have 2 ways of assigning values to pointed structures
(*g).wP = 5;
g->fP = 11;
You can also fit a variable into a structure definition
struct fraction {
int wP;
int fP;
} fract; //fract is a fraction structure
struct fraction newNum; //this creates a new fraction structure
With this, you can create aliases with typedef
typedef struct fraction {
int wP;
int fP;
} fract;
fract newNum; //this creates a new fraction structure
Dynamic memory also works as normal
//8 bytes taken (4 bytes per int * 2 ints)
g = (struct fraction *) malloc(sizeof(struct fraction));
Unions work exactly like structures, except for the fact that you can only use one element at a time
As a result, the memory taken up by a union is equal to the size of its largest element and not the total size of all the elements
union demographics {
int age /* 1st field is person’s age */
float salary; /* 2nd field is monthly pay */
double emplevel; /* 3rd field is rating scale */
char Owing; /* 4th field is office wing */
} employee; /* ending ; */
If I were to set age equal to something, we’d get the following result
employee.age = 24;
But if I were to set another element equal to something, it would overwrite whatever we assigned previously
employee.Owing = 'D';
realloc() is a way of resizing allocated memory
For example, let’s take the following pointer
int *a;
a = (int *) malloc(5 * sizeof(int));
a[0] = 4;
If I wanted to make the pointer size 9 instead of 5, I can do the following
a = (int *) realloc(a, 9 * sizeof(int));
printf("%d", a[0]); //4
This way, none of my original values are changed
The same applies if I want to decrease the space to size 3
a = (int *) realloc(a, 3 * sizeof(int));
The way this is done is simply extending the allocated memory down by the amount required
For our above example, this would mean extending down by ((9-5)*4) = 16 bytes