CS152
Chris Pollett
Sep 8, 2021
#include <stdio.h> char foo(); void bar() { printf("%c", foo()); } char foo( ) { return 'a'; } int main() { bar(); return 0; }
Generally, for each module (say .c file) of your program, you put all its function prototypes, struct definitions, #define's, global variable declarations, etc. into a header file (a .h file) for that program and then include it with the line like:
#include "foo.h" /* double quotes unlike <foo.h>, also searches current directory */
Since, you might use the same header file for several modules, the header file might have at its start some preprocessor code like:
#ifndef FOO_H #define FOO_H //header file code #endif // this prevents the header from being included more than once.
Roughly, pointers allow us to refer to a memory address.
int a, *b; /* b is declared as a pointer to an int memory address, (what * means when declaring a variable) */ a = 5; b = &a; /* now b points at where a is stored in memory (what & on a non-pointer means) */ printf("%d", *b); //prints 5. *b = 6; /* this changes the value of what's stored at the address b points to */ printf("%d", a); // prints 6.
char **p; // sorta like a 2d array.
int add(int a, int b) { return a+b; } int (* p) (int, int); p = &add; printf("sum:%d", (*p)(3,4) ); //output 7.
//main.c #include <stdio.h> //for printf statement #include <string.h> //has strlen #include "reverse.h" int main() { char hw[] = "hello world"; /* how to assign string literals in C Could also have done: char hw[12] = "hello world"; The notation hw[some_num] can be used to access some_num char. i.e., hw[3] == 'l' */ reverse(hw); printf("%s", hw); // want to output dlrow olleh return 0; }
gcc main.c reverse.c -o my_reverse
C has a mechanism for collecting together a bunch of existing data types into a new one using struct's. These can be thought of as classes without member functions.
struct Person { char name[12]; int age; }; //notice the ; struct Person p, *ptr; strcpy(p.name, "Bob"); //in string.h. Note p.name[3] = '\0' after // many useful string functions like strlen, strcmp, etc are in string.h p.age = 5; ptr = &p; printf("%d %s %s", p.age, (*ptr).name, ptr->name);
You can also declare:
struct { int a,b; } test; test.a = 5; /* so have declared a variable test but have not given the kind of struct it is a name */
The syntax struct Person p; of the last slide is sometimes awkward. To simplify it you can write:
typedef struct Person person_type; person_type a,b,c;
Using structs and pointers you can create recursive data structures:
struct mylist { int a; struct mylist *next, *prev; } test;
Remark: You can fake classes by using struct's which have function pointers as members.
union int_or_char { int my_int; //can hold either an int or a char but not both at the same time char my_char; // union allocates enough memory for the larger of the two possibilities } test; test.my_int = 8; typedef union int_or_char IntOrChar; IntOrChar test2;
enum suit {CLUBS, DIAMONDS, HEARTS, SPADES} suit_test; suit_test = DIAMONDS; typedef enum suit CardSuit; CardSuit suit_test2 = SPADES;
#include <stdio.h> #include <stdlib.h> //for malloc and free int main() { int *p; p = (int *)malloc(10*sizeof(int)); /*sizeof returns number of bytes an int takes (could do sizeof(person_type) for person_type of a couple slides back) */ if( p == NULL) { return 1; //bail out } /* do stuff. To refer to the location of ith int can do (p + i), its value is *(p + i) or p[i] */ free(p); // got to free or create a memory leak -- unlike Java no garbage collection return 0; }Notice we cast the result of malloc to be of type int rather than void*.
int a; scanf("%d", &a); //reads from stdin one int into a.
#include <stdio.h> int main(int argc, char * argv[]) //notice getting command-line args { int c; FILE *fp; if(argc < 2) { return 1; //bail if no file specified } fp = fopen(argv[1], "r"); // r is for reading, w for write, rb for binary, etc while ((c = fgetc(fp)) != EOF) { printf("%c", (char)c ); } fclose(fp); return 0; }
printf("Hit a key to continue"); c= getchar();
#include <termios.h> //... struct termios tio; tcgetattr( 0, &tio ); tio.c_lflag &= ~ICANON; tcsetattr( 0, TCSANOW, &tio ); printf("Hit a key to continue"); c= getchar();
make targetThe make utility would then search the current directory for a file called Makefile and then tries to satisfy the target goal.
A Makefile consists of rules of the form:
target1: depends_on1 depends_on2 ... <tab>command1 <tab>command2 ... <blankline> target2: depends_on1 depends_on2 ... #etc # is used for a single-line comment
Notice the use of tabs is important!
myprog: myprog.o cc -o $@ $< myprog.o: myprog.c cc -c -o $@ $< # $@ refers to the target $< refers to the first dependency clean: rm -f myprog myprog.o
You can declare variables in a Makefile using the format varname = value like:
CC = gcc SUBDIRS = io linkedlist
These variables could then be used:
all : $(SUBDIRS) $(CC) historylesson.c -o historylesson
An example of a multi-line make rule might be something like:
io : @echo "Making io..." cd io make all
%.o : %.c