Introduction to Programing in C

The C language is rather spare, and "close" to the underlying hardware. Actually, it might be more accurate to say that C is close to the 1970's PDP-11 hardware for which it was developed.

Basic Types

C contains the following basic types:

Any of these can be unsigned as well.

There is no Boolean type; integer values are used instead, with 0 representing FALSE and 1 (or any nonzero value) representing TRUE.

Some variable declarations:

int x=3, y=29;
long x = 1234567890123456l;     // last character is the letter "l"
int z = 0xdeadbeef;

Control Structures

Here are examples of if statements, and for and while loops. The parentheses around the conditions are mandatory.

if (x<0) {
    printf("negative");
} else if (x==0) {
    printf("zero");
} else {
    printf("positive");
}

In the while loop, we use i++, which means the same as i+=1.

int sum = 0;
int i = 1;
while (i<=100) {
    sum += i;
    i++;
}

int sum=0;
for (int i=1; i<=100; i++) sum += i;

Arrays

We can declare local arrays as 

    int  A[10];

The components of A are then A[0] through A[9].

We'll look at dynamically allocated arrays later, under Pointers.

Functions

These are very similar to Java:

int mysum(int n) {
    int sum = 0;
    for (int i=1; i<=n; i++) sum += i;
    return sum;
}

Pointers

We can declare p to be a pointer to an integer with

int * p;

This might best be thought of as needing to be read right to left: p is a pointer (the *) to an int.

If p points to an integer, then *p is the integer pointed to, so the above can also be seen as "int (*p)"; that is, *p is an int, and that makes p a pointer to int.

What can p point to? We can create an int object on the heap, with malloc. This takes a parameter equal to the number of bytes to be allocated, and returns a (void *). We need a cast to convert it to (int *):

int * p = (int *) malloc(sizeof(int));
*p = 3;

However, and completely unlike Java and C#, we can also create pointers to stack objects (local variables), using the address-of operator &:

int x = 3;
int y = 4;
int * px = &x;
int * py = &y;
*px = 5;
px = py;
*px = 6;
printf("x=%d, y=%d\n", x, y);

Make sure you understand the output! x=5, y=6.

The & operator can be used to return pointers to local variables. This is very bad! Don't!

int x = 37;
int * p = &x;
return p;        // BAD

If we define a struct myobject (never mind how; this is like an object, but with only data elements, not methods), we can allocate one on the heap as follows. This contains a type cast; see below.

int * p = (struct myobject *) malloc(sizeof(struct myobject));

The weird aspect of C pointers is their interaction with C arrays. If a pointer p points to an element of a C array, and is incremented, then it points to the subsequent element. In the following, the final entry in the array A has the value 0.

int A[10] = {1,2,3,4,5,6,7,8, 9,0};
int * p = & A[0];        // or int * p = A; see below
int sum = 0;
while (*p != 0) {
    sum += *p;
    p++;
}

Note that p++ increments the pointer, not the thing pointed to. If we want to increment the thing pointed to, we need (*p)++. Worse, *p++ is not the same; it means to return the thing pointed to, but also increment p itself, afterwards. We will avoid this usage, though it was once thought to be very clever in the C world.

A string in C is an array of char, where the final byte is 0. We can get the length of a string this way:

int strlen(char * p) {
    int len = 0;
    while (*p != 0) {
        len++;
        p++;
    }
    return len;
}

Two other classic string-manipulation functions are strcpy() and strcmp(). Note that strcpy() has no check to see if any space has been allocated at dest!

char * strcpy(char * dest, char * src) {
    char * ps = src; char * pd = dest;
    while (*ps != 0) {
        *pd = *ps
        pd++;
        ps++;
    }
    *pd = 0;    // null termination
    return dest;
}

Strcmp() returns -1 if the first string is alphabetically less than the second, 0 if they are equal, and +1 if s1 is alphabetically greater than the second.

int strcmp(char * s1, char * s2) {
    while (*s1 != 0) {
        if (*s1 != *s2) break;    // different chars found, possibly null
        s1++;
        s2++;
    }
    unsigned char c1 = *s1;        // this makes the comparison easier
    unsigned char c2 = *s2;
    if (c1 < c2) return -1;
    if (c1 == c2) return 0;
    return 1;
}

We could replace the if statements at the end by return c1 - c2, as only the sign of the result matters.

Casts

It is often necessary to convert from one type to another in C. This is generally done via a cast, which involves enclosing the type expression in parentheses. For example:

double d;
int x = (int) d;

When converting between int and double, there is actually some work to be done. However, in many cases there is no actual conversion; it is simply a matter of getting the compiler to cooperate. For example, note the cast above in creating a pointer to myobject. The return type of malloc(), in fact, is (void *), so it must be cast to be of any use at all.