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