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.
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;
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;
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;
}
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.
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.