Home CPSC 305

C Programming Continued

 

Structures

A structure in C provides a way to group multiple pieces of data together. They are like classes in Java and C++, except they can only contain data, and everything is public.

Below is a structure representing a 2D point:


struct Point {
    int x;
    int y; 
};

We could then create a Point object, and assign its data members like so:


struct Point origin;
origin.x = 0;
origin.y = 0;

Note that the "struct" keyword must also be given when declaring structures, unlike in C++ or Java classes!

When declaring a structure, you can also initialize its values:


struct Point origin = {0, 0}; 

This only works for initialization. You can't use this syntax to update the fields of a structure.

Structures can also be passed into functions as follows:


void point_print(struct Point p) {
    printf("(%d, %d)\n", p.x, p.y);
}

Below is a complete program using this structure:


#include <stdio.h>

struct Point {
    int x;
    int y; 
};

void point_print(struct Point p) {
    printf("(%d, %d)\n", p.x, p.y);
}

int main() {
    struct Point origin = {0, 0};
    point_print(origin);

    return 0;
}

 

Pointers to Structures

Because structures can be large, they are frequently passed as pointers. We could rewrite the point_print function to take a pointer like this:


#include <stdio.h>

struct Point {
    int x;
    int y; 
};

void point_print(struct Point* p) {
    printf("(%d, %d)\n", (*p).x, (*p).y);
}

int main() {
    struct Point origin = {0, 0};
    point_print(&origin);

    return 0;
}

Because the . operator in C has higher precedence than the * operator, we would need to use (*p).x to read the fields. *p.x would not work.

Because this syntax is so ugly, for such a common operation, C has an alternative syntax which is the -> operator. This can be seen in this example:


#include <stdio.h>

struct Point {
    int x;
    int y; 
};

void point_print(struct Point* p) {
    printf("(%d, %d)\n", p->x, p->y);
}

int main() {
    struct Point origin = {0, 0};
    point_print(&origin);

    return 0;
}

pointer->field means to dereference pointer and read field from the structure at that address.


 

Type Definitions

Some programmers do not like having to use the "struct" keyword whenever declaring a structure. To get around this, we can use the typedef keyword in C.

A typedef statement contains a type and also an alias. For instance, we can create an alias for an character pointer called string:


typedef char* string;

After that typedef, "string" can be used as an alias for "char*".

When done with a struct this looks like this:


typedef struct Point Point;

The declaration of the struct and the typedef can also be combined into one declaration:


typedef struct {
    int x;
    int y;
} Point;

After doing this, we could just declare a Point as:


Point origin;

 

Debugging

The gdb debugger is available for C programming. In order to use it, we must compile our code with debugging symbols present. This makes our code less efficient, but makes the debugger more helpful. This is done by passing the -g flag to gcc:

gcc -g program.c

We then can run the program inside of gdb:

gdb a.out

We can then use the following commands to inspect our code:

CommandMeaning
break XSet a breakpoint in the code. Replace "X" with the name of a function, or a line number
runBegin running the program from the beginning
stepMove to the next line of the program, stepping into functions
nextMove to the next line, stepping over function calls
continueRun until the program finishes or hits a breakpoint
print XPrint the value of variable or expression X
call XMake a function call to function X
wherePrints the current stack trace
upGo one level up the stack
downGo one level down the stack
listShow the code for where we are
quitQuit out of gdb

The most basic usage of gdb is to see what's causing a segmentation fault. If we run our program in gdb and it segfaults, we can use the "where" and "list" commands to see the code that caused the crash, as well as print out any variables that may be involved.


 

Command Line Arguments

C programs can take command line arguments which is the text given after the name of the program when you run it (if any). They are passed as arguments to main.

The first argument is an integer giving the number of arguments which were passed. The second is an array of character strings giving the actual arguments. Because arrays are passed as pointers, and strings themselves are arrays, this is actually a pointer to a pointer.

This program prints all of its arguments:


#include <stdio.h>

int main(int argc, char** argv) {
    int i;
    for (i = 0; i < argc; i++) {
        printf("Argument %d is '%s'\n", i, argv[i]); 
    }
    return 0;
}

An example run is given below:

[finlayson@cs ~]$ a.out hello there
Argument 0 is 'a.out'
Argument 1 is 'hello'
Argument 2 is 'there'

Note that the name of the program itself is the first argument!

Arrays of strings, and 2D arrays are instances where we will use pointers to pointers.

Copyright © 2024 Ian Finlayson | Licensed under a Attribution-NonCommercial 4.0 International License.