Introduction
Announcements

Schedule
Labs
Assignments
TA office hours

Tests, exam

Topic videos
Some course notes
Extra problems
Lecture recordings

Discussion board

Grades so far

C data types

Fundamental C data types include int, long, double, rarely float;
char is just a small int.

"literals":

38
value 38, type int
38L
value 38, type long
0x2a
value 42, type int
033
value 27, type int
(Is the value really decimal 27 or really octal 33? Wrong, it is both. Those are the same number!)
0.3
type double
3.0
type double
6.02e23
type double

In C, most things are ints.
Exaggeration, but true originally

Mixed mode arithmetic

5.3 / 60
yields type double
and it does real division. Even though 60 is an int.

All normal programming languages have what we call "mixed mode arithmetic". Not new. You can combine reals and ints in an expression.

double + double -> double (obviously)
int + int -> int (obviously)
double + int -> double
int + double -> double
(any operator)

Try example: 3 + 4.2 * 6 / 3 + 2 * 4 * 8.1

indicate which operators are double and which are int. n.b. it matters most about the division.

Arrays

To discuss the way the C programming language views memory and data types, we have to replace the very-high-level "memory model" you have from first year with a much lower-level memory model.

Memory is a vast array of bytes. Each byte has an address.

Bytes are collected into words. These days a byte is often 8 bits and a word often 4 bytes or 32 bits.

Thus in the two's-complement representation which you may know from CSC 258, a word can represent an integer from -231 to 231-1, inclusive.

In Java, an array is an object. In C, an array is just a list of the item it's an array of. So in C, if an int is four bytes, then an array of ten ints is forty bytes. Nothing extra.

This is why, as mentioned last day, we won't get a diagnostic for exceeding array bounds. It's up to us, not the execution environment, to keep track of how big that array is. If we declare "int a[10];", then after a[9] (the last element in the array, counting from a[0] as the first), there is more memory there, because memory is a vast array of bytes; but we don't know what it is. It might be another variable in our program. It might be something else. It's an error to access it. But we won't get a nice java traceback. We'll get what the ANSI C standard calls "undefined behaviour", which means that it might:

Array elements are guaranteed to be adjacent. If the address of a[0] is 100, then if each element is four bytes, the address of a[3] is 112. (a[0] is at 100; a[1] is at 104; a[2] is at 108; a[3] is at 112.)

In general, for an array we have a base address and an element size. Then the address of element #i is base + elementsize * i. The existence of this address-calculation formula allows the compiler to compile expressions such as a[i] where i may vary; not only expressions such as a[3].

"pointer": high-level version of an "address"
        -> has type information

int i;
int *p;    -> declare p to be of type pointer-to-int

i = 3;
p = &i;   -> assign p to point to i
printf("%d\n", *p);   -> "dereference" -- follow a pointer
i = 4;
printf("%d\n", *p);
so "&" and "*" are opposites.

"pointer arithmetic":

p + 3 -- yields the address which is three ints later, i.e. address(p) + 12

So if p contains the address of a[0], p+i is the address of a[i].

For two expressions x and y, x[y] is defined as *((x) + (y))

So, how is this going to work with arrays, if we haven't declared any pointer variables?

An array name in an expression context decays into a pointer to the zeroth element.

a[3]

Array parameters

Consider the following:
#include <stdio.h>

int main()
{
    int a[10];
    extern void setsquares( ... what goes here? ... );

    setsquares(a);
    printf("three squared is %d\n", a[3]);
    return(0);
}

void setsquares( ... what goes here? ... );
{
    int i;
    for (i = 0; i < 10; i++)
        a[i] = i * i;
}
What goes there?

Remember that an array name in an expression context decays into a pointer to the zeroth element.

So when we call "setsquares(a)", what's being passed is a pointer to the zeroth element of "a". That is, it's of type pointer-to-int.

    extern void setsquares(int *a);

...

void setsquares(int *a)

You may have seen people using "int a[]" or "int a[12345]" instead of "int *a" there. This works because there is a special weird rule for formal parameters only: You can write void setsquares(int a[]) and it is converted to a pointer declaration.

I strongly recommend against this.
It is still a pointer!
This weird conversion rule is specific to formal parameter lists!

So we will write "int *a" in this situation. Because it really is a pointer. I recommend this practice both in this course and in real life. Outside of a formal parameter declaration, "int *a" and "int a[10]" are completely different... and "int a[]" is illegal.

Given java background it might be quite attractive to write "int a[]". However, in C it is a pointer, not an array. In java, it's an array, it's an object reference, it's quite a different situation.

Similarly, in C's early days a lot of people liked to write "int a[]" in formal parameter lists because it resembled the syntax in C's precursor "NB" (New B, a later version of the programming language B). But I, and many other modern C programmers, think that this is confusing and unnecessarily far away from saying what you mean. It's really a pointer.


Now, arrays in C are not objects; they are just a concatenation of n of the array element items. So you cannot tell, when you have a pointer to the zeroth element of an array, how large that array is. (This remains true even if you use the array-like parameter declaration syntax that I recommend against above.)

So most of the time when you pass an array to a function, you also pass an int which is the size of the array.

Thus altogether this program would probably look more like this:

#include <stdio.h>

int main()
{
    int a[10];
    extern void setsquares(int *a, int size);

    setsquares(a, 10);
    printf("three squared is %d\n", a[3]);
    return(0);
}

void setsquares(int *a, int size)
{
    int i;
    for (i = 0; i < size; i++)
        a[i] = i * i;
}