The C Language

Resources

Thousands. Books, tutorials, references.... Two, one local and one at random from web somewhere, are

C and Unix Notes

C Intro PPT slides

IS C Paradigmatic?

Yes, of : "Von Neumann Languages": FORTRAN, BCPL, PL/I, Ada, Pascal, ...

Languages and the Von Neumann Computer

Typical assignment in typical language: right hand side is easily formalizable with mathematics and thus stands a chance of meaningful semantical analysis (what it means, is it correct?).
A = f(g(a+h(b)+cv)+g(a))

But "Statements" hard to formalize: for while switch if ... elsif... if ... then ... else
FORTRAN, BCPL, C... mirror the computer's instructions and architecture -- thus easy to compile, efficient code, and paradigmatic of the Von Neumann computer.

What Does C Look Like?



                                  #include\
                                  
                     #include                
                     #include                

                    #define w "Hk~HdA=Jk|Jk~LSyL[{M[wMcxNksNss:"
                   #define r"Ht@H|@=HdJHtJHdYHtY:HtFHtF=JDBIl"\
                  "DJTEJDFIlMIlM:HdMHdM=I|KIlMJTOJDOIlWITY:8Y"
                 #define S"IT@I\\@=HdHHtGH|KILJJDIJDH:H|KID"\
                "K=HdQHtPH|TIDRJDRJDQ:JC?JK?=JDRJLRI|UItU:8T"
               #define _(i,j)L[i=2*T[j,O[i=O[j-R[j,T[i=2*\
              R[j-5*T[j+4*O[j-L[j,R[i=3*T[j-R[j-3*O[j+L[j,
             #define t"IS?I\\@=HdGHtGIDJILIJDIItHJTFJDF:8J"

    #define y                  yy(4),yy(5),                yy(6),yy(7)
  #define yy(              i)R[i]=T[i],T[i ]            =O[i],O[i]=L [i]
#define Y _(0          ], 4] )_ (1 ], 5] )_ (2      ], 6] )_ (3 ], 7] )_=1
#define v(i)(      (( R[ i ] * _ + T [ i ]) * _ + O [ i ]) * _ + L [ i ]) *2
double b = 32  ,l ,k ,o ,B ,_ ; int Q , s , V , R [8 ], T[ 8] ,O [8 ], L[ 8] ;
#define q( Q,R ) R= *X ++ % 64 *8 ,R |= *X /8 &7 ,Q=*X++%8,Q=Q*64+*X++%64-256,
# define  p      "G\\QG\\P=GLPGTPGdMGdNGtOGlOG"   "dSGdRGDPGLPG\\LG\\LHtGHtH:"
#  define W         "Hs?H{?=HdGH|FI\\II\\GJlHJ"    "lFL\\DLTCMlAM\\@Ns}Nk|:8G"
# define   U           "EDGEDH=EtCElDH{~H|AJk}"       "Jk?LSzL[|M[wMcxNksNst:"
#  define u                  "Hs?H|@=HdFHtEI"             "\\HI\\FJLHJTD:8H"
char  *   x                   ,*X , ( * i )[               640],z[3]="4_",
*Z = "4,8O4.8O4G" r U "4M"u S"4R"u t"4S8CHdDH|E=HtAIDAIt@IlAJTCJDCIlKI\\K:8K"U
 "4TDdWDdW=D\\UD\\VF\\FFdHGtCGtEIDBIDDIlBIdDJT@JLC:8D"t"4UGDNG\\L=GDJGLKHL\
FHLGHtEHtE:"p"4ZFDTFLT=G|EGlHITBH|DIlDIdE:HtMH|M=JDBJLDKLAKDALDFKtFKdMK\
\\LJTOJ\\NJTMJTM:8M4aGtFGlG=G|HG|H:G\\IG\\J=G|IG|I:GdKGlL=G|JG|J:4b"W
S"4d"W t t"4g"r w"4iGlIGlK=G|JG|J:4kHl@Ht@=HdDHtCHdPH|P:HdDHdD=It\
BIlDJTEJDFIdNI\\N:8N"w"4lID@IL@=HlIH|FHlPH|NHt^H|^:H|MH|N=J\\D\
J\\GK\\OKTOKDXJtXItZI|YIlWI|V:8^4mHLGH\\G=HLVH\\V:4n" u t t
"4p"W"IT@I\\@=HdHHtGIDKILIJLGJLG:JK?JK?=JDGJLGI|MJDL:8M4\
rHt@H|@=HtDH|BJdLJTH:ITEI\\E=ILPILNNtCNlB:8N4t"W t"4u"
p"4zI[?Il@=HlHH|HIDLILIJDII|HKDAJ|A:JtCJtC=JdLJtJL\
THLdFNk|Nc|\
:8K"; main (
int C,char**        A) {for(x=A[1],i=calloc(strlen(x)+2,163840);
C-1;C<3?Q=_=       0,(z[1]=*x++)?((*x++==104?z[1]^=32:--x), X =
strstr(Z,z))      &&(X+=C++):(printf("P2 %d 320 4 ",V=b/2+32),
V*=2,s=Q=0,C     =4):C<4?Q-->0?i[(int)((l+=o)+b)][(int)(k+=B)
]=1:_?_-=.5/    256,o=(v(2)-(l=v(0)))/(Q=16),B=(v(3)-(k=v(1)
))/Q:*X>60?y   ,q(L[4],L[5])q(L[6],L[7])*X-61||(++X,y,y,y),
Y:*X>57?++X,  y,Y:*X >54?++X,b+=*X++%64*4:--C:printf("%d "
,i[Q][s]+i[Q ][s+1]+i[Q+1][s]+i[Q+1][s+1])&&(Q+=2)&V||(Q=
0,s+=2)<640
||(C=1));}


Our little example...

Obfuscated C Code Contest winners

This program takes a single command line argument, transcribes the argument text into Tolkien's Elvish letters, and writes the transcription to standard output as a portable graymap (PGM) file.

The specific transcription mode is the Black Speech Tengwar mode, as used in the inscription on the One Ring. The program handles the following diagraphs:

- "gh", "sh", "th": the corresponding tengwar are output for these specific sounds - "mh" denotes the full tengwa for [m]; "m" by itself means a horizontal bar above the next letter - "rh" denotes the [r\] tengwa - "uh" denotes the tehta for long vowel [u:] - "zh" selects the up-reaching tengwa for [z], while "z" selects the down-reaching one

Punctuation marks other than commas and periods are ignored, as are whitespace characters. In addition, the argument text must be entirely in lower case.

The following command thus writes a rendering of the Ring inscription to the file out.pgm: ./anonymous "ash nazg durhbatuluhk, ash nazg gimbatul, ash nazg thrakatuluhk, agh burzhumh-ishi krimpatul." >out.pgm

Why C?

C OVERVIEW

Very close to machine-code level. E.g. constructing pointers to anywhere.

By Java standards, few declarations and less type checking. Fewer ``built in'' standard libraries.

Lots of legacy code around.

Possible to construct object-oriented code, modular programming style (e.g. Quake II game engine).

Besides a few idiosyncrasies, a good representative imperative language, used in lots of future CSC courses.

E.g. Matlab language is very like C with huge built-in libraries of specialized functions. (Matlab has structures and some inheritance pasted on, but very limited use of pointers (aka handles)).

C And JAVA

#include <stdio.h> main(int arg, char *argv[]) { if (argc == 2) printf("Hello %s\n",argv[1]); } Class Hello { public static void main (String args[]} { if (args.length == 1) { System_out print ("Hello"); System_out println (args[0]); }}

C VS. JAVA (RECURRING THEME)

C Facts

Top-level program always called main. Lots of separate files used and needed for big programs. Hence make.

storage classes (with separate compilation of files)
(global) static
(local) static
extern
const, hence
#include
--- typedefs and externs in .h file
--- matching globals in *one* .c file each

I/O
#include <stdio.h> printf, scanf, puts, gets, putchar, getchar FILE *fopen (const char* filename, const char *mode) fprintf, fscanf, fputs, fgets, fputc, fputc, fwrite, fread, sprintf, sscanf

C-isms

Data Types, Control, Arithmetic, Conditionals, Functions, Structures, Strings, Bit Operations, I/O -- all bog-standard.

Scope: Global, Static (to file) , Automatic (Local to function).

Makefile: organize the compiling and linking given edits and changes. (make, gmake?). General tool for stating dependencies and how to resolve them. Latex, your experiments...whatever. Like Prolog??

Character Strings: strings.h library, ended by null character \0.

Type Conversion...via cast or "what you expect". Not as pernicious as Matlab's, much looser than Java!

Preprocessor, #include, and .h files. Needed because even simplest I/O or mathematical functions need their own libraries (!)
#include <stdio.h> #include <math.h>

I/O: stdin, stdout, stderr, getchar, putchar, scanf, printf, fopen, getc, putc, fclose, feof, fscanf, fprintf, sscanf, sprintf. Matlab's are a subset, work similarly but not exactly.

Memory Management (calloc, malloc)

Command Line Arguments -- as in Java

Types

No boolean type: use ints or null pointer.
Structs are like classes without methods, and with all-public fields declaration syntax:
--- read right as far as possible, then left,
--- then out one level of parentheses (if any) and repeat.
Egs:
struct foo *bar[10];
bar is 10-elt arr of ptrs to foo structs
const char *s;
s is ptr to chars that are constant, but
char * const t;
t is a constant pointer to chars
typedef: gives names to struct definitions
Very Useful with the "enumerated" data type operator enum :
typedef enum {UP, DOWN, LEFT, RIGHT} direction;

Structures, Pointers...

C has in-line allocation of structured objects (structs). A pointer is an address, and to "dereference" the pointer is to retrieve what is at that address. Very common in C programs.

In Java. non-basic types (i.e. all classes and all arrays) dynamically allocated. A variable of a non-basic type is a *reference* to the dynamically-allocated space: recursive types natural:

class listnode { mytype data; listnode next; }

In C all types (including structs and arrays) are allocated in place by default. Recursive types don't make sense. Use explicit POINTER type: struct listnode { mytype data; struct listnode * next; }

E.G. SEARCH FOR ELT IN LIST

struct node { int datum; struct node *next; } node *find (int d, node *head) { while (head) { if (head->datum == d) /* or ((*head).datum == d) not 'X = d'! */ return head; head = head->next; } return 0; }

MEMORY MANAGEMENT (NO GARBAGE COLLECTION)

Use calloc or malloc, sizeof, free.

calloc, malloc are very similar:

void free; void *malloc(); /* also calloc() */ int *foo = (int *) malloc (10 * sizeof (int)); ... free (foo);} /* 'memory leak' otherwise */

MEMORY LEAK

Allocate space for 1000 ints. #include < stdlib.h > int * MyKintsPtr; MyKintsPtr = (int *) calloc(sizeof(int), 1000); /* memory contents zeroed by calloc */ MyKintsPtr = (int *) malloc(1000*sizeof(int)); /* (whoever's) old memory contents preserved */ /* we lost pointer to first 1000-long array! */ free(MyKintsPtr); /*no help, still a memory leak*/

UNIX Tools

File manipulation, utilities ( awk, sed, yacc, lex, grep ), pipes (|), I/O redirection (>, <, etc.), shell and shell scripts.

See the Primary Readings, Books at Carlson Reserve, etc. cd, cat, ls, mv, cp, mkdir, chmod, less, file redirection (>,<,>>,...),
ssh,
``dot'' files: list with ls -a :
.cshrc .login .cshrc .emacs

Job control, command-line edits, grep, rcs (ci,co, rcs, rlog), enscript, psnup, whatever.

EDITORS, DEBUGGERS, MAKE

EMACS: See Primary Readings, many books, on-line tutorial, info mode of emacs...

gdb (read the emacs info page!):
list; break, watch; run, cont, next; where; print, display; whatis; ptype ...

Here's a little GDB example session .

MAKE: command and makefile to control flow of jobs. Generally useful (run big experiments, manage resulting files, etc...). For us, does housekeeping while building programs. Keeps track of dependencies, only compiling files that depend on the one you just edited.

Small examples will follow. See Primary Readings for extended example and things to look for. Also the emacs info page has info on make. Here's something for example... U. Md. make Tutorial.

MORE ON POINTERS

At a hardware level, "indirect address" instructions ARE pointer instructions.

Variable: a memory location containing a numerical value.
Pointer: a memory location that contains an address.

Address Operator & applies to a variable, returns address of that variable:
ptr_v = &x;

Indirection Operator * applies to pointer, retrieves value from memory location held in that pointer (i.e. retrieves what the pointer ``points to'').
*ptr_v = 699;

scanf for input can't affect its arguments outside, so must use pointers. scanf("%d %d", &r, &c); /* more basic egs: */ short x = 33; short *sptr; sptr = & x; *sptr = -123; Must declare pointer and the type of its variable!
double *dptr; double* d2ptr; char *malloc();

Example: Pointers

C is strictly call by value, so this does NOT work! void Swap_it(int u,v); { int temp; temp = u; u = v; v = temp; } So we implement call-by-reference like this:
/* this takes values of addresses in, swaps what's IN those address values*/ void SwapByRef(int *u, int *v); /*take THIS, Call By Value! */ { int temp; temp = *v; *v = *u, *u = temp; } main() { int x=10, y = 2; SwapByRef(&x, &y); }

ARRAYS AND POINTERS

/* VARIABLE DEFINITIONS (global or local) (these allocate space):*/ int foo[100]; /* 100-elt arr of ints */ int *foo; /* ptr to int (or elt of int arr) */} /* code: */ foo[10] above works with EITHER variable definition: correct with latter only if foo refers to an array with at least 11 elements (indices start at zero, as in Java).

PARAM DECLARATIONS (in function prototype [header]) (these do NOT allocate space): int foo[100] /* ptr to 100-elt int array */ int foo[] /* size irrelevant */ int *foo /* same as above*/ Declarations of extern arrays allow missing sizes, as in parameter declarations.

EXAMPLES

2D v. row-pointer array layout:
/* variable definitions:*/ int foo[10][10]; /* 10x10 arr of ints, contig. layout */ int *foo[10]; /* 10-elt arr of ptrs to ints. a DIFFERENT LAYOUT, more like Java */ /* code: */ foo[3][4] /* works with EITHER definition */ parameter declarations: int foo[10][10] /* 10x10 arr of contig. ints*/ int foo[][10] /* first dimension size irrelevant */ int foo[10][] /* ERROR: must know size of 2nd dim. */ int *foo[10] /* row-pointer layout */ int *foo[] /* don't have to know size*/ int **foo /* same as above */

POINTERS AND ARRAYS Again...

Political Incorrectness: we know array indexing implemented with pointer arithmetic. So....
*(mat + i) = 123;
is (basically) same as
mat[i] = 123;.
Here invisible type-conversion is maybe needed, don't know if it happens. If i is compiled meaning "integer" and it's an array of "bytes" then....:-}. Thus not a great style. Matlab has similar problems with "too much information" instead of "information hiding".

Declaring an array implicitly declares pointer to its zeroth element: the array name itself!

So C passes arrays to functions by reference:
int MyArr[MAX]; SortArr(MyArr, MAX); could return sorted array in MyArr, whereas we know we can't affect integer arguments outside!

STRUCTS and Struct pointers

/* declare a struct */ struct date { int month; int day; int year; }; struct date today; /* create a struct named today AT COMPILE TIME*/ today.day = 21; /* update a field */

As usual, structures of arrays and vice versa, structures within structures, etc. all as expected. Usually want pointer to struct, not its name as above, since they may not have names, it's boring to declare 100 structs, and we often have a data structure full of anonymous structs that point at each other.

struct date *date_pointer; /*assign it to some date struct, then */ (*date_pointer).day =21; /* correct syntax, but yikes!*/ date_pointer -> day = 21; /* so... nicer syntax */

Structure Example: Matrix Addition

In the bad old days, you had to write your own matrix package! This is part of mine... The typedef statement lets us create a new type-name for pointer to our struct. typedef struct matrix { int r; /* rows*/ int c; /* cols */ dmat mat; /* rows*cols numbers */ } *matrix_t; /*here's the new pointer type*/ /* Matrix Addition Function: Return +- sum of 2 args */ /* Olllld Syntax! (lurking in CB's code)*/ matrix_t mat_add(a,b,sign); /* args*/ matrix_t a,b; double sign; /* their types*/ /* ANSI Syntax */ matrix_t mat_add (matrix_t a, matrix_t b, double sign); { int i,j; matrix_t sum; if ((a->r != b->r) || (a->c != b->c)) { { cberror("mat_add: bad sizes"); return (NULL);} sum = mat_new(a->r, a->c); if (sum ==NULL) { cberror("mat_add: memory?"); return NULL;} for (i=0; i = a->r; i++) /* ++ increments by 1: very C-ish */ for (j=0; j = a->c; j++) sum->mat[i][j]= a->mat[i][j]+sign* b->mat[i][j]; /* don't need {} around single-statement for-loops. */ return sum; }

Example From The Scanner (Assignment!)

int get_character(location_t *loc) { int rtn; if (loc->column >= loc->line->length) { return 0; } rtn = loc->line->data[loc->column++]; if (loc->column >= loc->line->length && loc->line->next) { loc->line = loc->line->next; loc->column = 0; } return rtn;

Again: SCANF Meets (Needs) STRUCTURE POINTER

... m = mat_new(int r, int c); /* m points to new struct with rows, cols filled in, no data */ for (i=0; i<r; i++) { for (j=0; j<c; j++) scanf("%lf",&((m->mat)[i][j])); /*fills up data matrix of struct*/ } return m; /* or use it... */

TYPE CONVERSION

Some is automagic: e.g. arith. ops with floats and ints

Explicit casting: average = (float) total / n;

Casts are rather common: a =(matrix_t) malloc ((unsigned) sizeof(struct matrix))

Explicit Makefile

It's obvious what we want done with most files --an executable file implies we have to compile and link source files, C files want a C compiler, etc.. There are highly-evolved conventions, defaults, shorthand, etc. exist. Not below.

targets (actions to be completed) on left before colons. TABS are part of syntax!! that's not just white space. What the target needs done to make it is on the right. So make(clean) at top level removes all the object files. OBJS = scanit.o char_classes.o INCS = char_classes.h scanit.h FLAGS = -g runtest: $(OBJS) gcc -o runtest $(FLAGS) $(OBJS) scanit.o: scanit.c $(INCS) gcc -c scanit.c clean: rm *.o

Makefile with Implicit Rules

That backslash is the makefile line-continuation operator... line breaks and tabs aren't just whitespace. LIBS = -lm ### compiler control ### CFLAGS = -g CC = gcc #------------------------------------------------- track: lattice.o driver.o ${CC} ${CFLAGS} lattice.o driver.o \ ${LIBS} -o pred lattice.o: lattice.c driver.o: driver.c

Example: Rantest.c

Disclaimer: most of this code sort of worked sometime. Normal C compiler nowadays is "gnu C". ANSI C is pretty portable, but typos, bugs, etc. always exist! Also they are written by CB, probably the absolute last programming stylist a sane person would ever consult. Take these for what they're worth.

#include < stdio.h > #include < math.h > /*libraries*/ #include "cbtypes.h" #include "cbmath.h" /*yours*/ double Contaminated_Normal(); /* returns a double */ main() { int i,j, rseed; double p,m2,s2,m,s,n; set_seed(); /*initializes a random generator*/ for(j=0; j<3; j++) { printf("\np, mean1, std1, m2, std2:"); scanf("%lf %lf %lf %lf %lf", &p, &m, &s, &m2, &s2); for (i = 0; i<20; i++) { n = Contaminated_Normal(p,m,s,m2,s2); printf("\n variate: %f ", n); }}}

Contaminated_Normal.c

#include < stdio.h > #include < sys/time.h > #include < math.h > #define SAMPLE_SIZE 2147483647 /* range of random generator */ #define ROOT12 3.4641016151377544 long random(); /* library function */ char *malloc(); /* do. common, still must declare */ double N(); /* Contaminated_Normal: for fraction p of time it is normal of (m1,std1). (1-p) of the time it is normal of (m2, std2) */ /*here we go -- Finally...*/ double Contaminated_Normal (p,m1, std1, m2, std2) double m1,m2, p, std1, std2; { double fac,u1, u2, v1, v2, s; long u; static int iset = 0; static double gset; u = random(); u1 = ((double) u ) / SAMPLE_SIZE; /*cast!*/ if (u1 <= p) return N(m1,std1); else return N(m2, std2); }

Preprocessor, .h files

#define /*more macros...*/ #include /* for conditional compilation, say...*/ #ifdef, #endif, #else, #ifndef #if, #elif

MATRIX.C

/* defining some useful matrix data structures, routines... /* #include < stdio.h > #include < math.h > #include "cbtypes.h" #include "cbmath.h" /* we'll see this next*/ /* externally-defined names redeclared */ char *malloc(); /* lest we forget */ void cberror(msg); /* here we go...*/ char msg[]; { void exit(); fprintf(stderr, "\nError: %s, old buddy.", msg); } /***simplified versions with ranges [0,N-1] **/ /*Vectors are columns*/ ivec ivector(n) int n; { return (ivec) malloc((unsigned) n*sizeof(int)); } ...

cbmath.h

#ifndef _cbmath_h #define _cbmath_h /* common C-ism: define unlikely-named variable the first time this file is processed: after first time, other mentions are ignored */ /*****important matrix macro-- saves lots of typing *****/ #define e(m,i,j) m->mat[i][j] /******some basic macros ********/ #define max(x,y) x>y?x:y #define min(x,y) x>y?y:x /* #define abs(x) x>0?x:-x */ #define deg_to_rad ( (double) M_PI/180.0) /*external matrix stuff defined elsewhere*/ extern void cberror(); extern int lubksb(); extern int lubcmp(); extern void ivec_free(); extern void mat_free(); /*****error codes*****/ #define OK 1 #define MALLOC 0 /****general matrix types ****/ typedef double *dvec; typedef int *ivec; typedef double **dmat; typedef struct matrix { int r; int c; dmat mat; } *matrix_t;

Why We Love Matlab: 2-D arrays

Unlike polymorphic Java, we would need diff. functions for ints, doubles....!!
Also, if declare, say double M[4][4] then need 4x4 matrix ops. What about 1x4 or 5x5?? :-{.
We do this:

dvec dvector(n)
int n;
{
 return (dvec) 
      malloc((unsigned) n*sizeof(double));
}

/* create matrix by allocating r  
  c-long vectors for the rows,
   a vector of pointers to rows, 
and a pointer to the latter vector. */

matrix_t mat_new(int r,int c)  
               /*r rows, c cols*/
{
int i;
dmat m;
matrix_t a;
 m = (dmat) malloc( r*sizeof(double *));
 if (m == NULL) return NULL;
 for (i= 0; i < r; i++)
 {
 m[i] = (dvec) malloc( c*sizeof(double));
 if (m[i] == NULL) return NULL;
 }
 a =(matrix_t) malloc ( sizeof(struct matrix));
 if ( a == NULL) return NULL;
 a->r = r;
 a->c = c;
 a->mat = m;
 a-> fev_type = MATRIX;
 return a;
 }

/*  can't just free the pointer
 to the matrix, there's 
no garbage collection */

void mat_free(m)
matrix_t m;
{
int i,r;
r =m->r;
for (i = 0; i < r; i++) free ((char *) m->mat[i]);
free((char *) m->mat);
free((char *) m);
}

COMMAND LINE ARGUMENTS

Definition of main:
int main (int argc, char *argv[])}

So argv is an array that contains elements that are ``pointer to char''s, or strings. When main is called, the first arg gets assigned the number of args on command line. The second 'argument vector' has argc+1 entries, with [0] being a pointer to the name of the program executing, and the rest are pointers to the strings on the command line. So can check on number of args, if wrong or an error (say no file by a requested name), print message and ``how to invoke'' help... e.g. on command line at top-level:
CopyFile FromFile ToFile