CSC209H Lecture 3 Dan Zingaro January 21, 2015
Streams (King 22.1) Stream: source of input or destination for output We access a stream through a file pointer (FILE *) Three streams are available without us doing anything stdin: standard input (keyboard unless redirected) stdout: standard output (screen unless redirected) stderr: standard error (screen unless redirected) To use other streams, we can open files
Opening and Closing Files (King 22.2) #include <stdio.h> //mode r=read, w=write, a=append FILE *fopen(const char *filename, const char *mode); int fclose(file *stream); fopen returns NULL if it fails fclose returns EOF if it fails When a function call fails, we must detect it or the program will keep running and crash much later
Error-Handling System and library calls often return a special value (NULL, EOF, -1, etc.) to indicate an error If there is an error, they set variable errno to a positive integer error number errno is initialized to 0 when the program starts running If a call fails, the value of errno is meaningful
Perror void perror (char *str); perror writes the following to standard error: str, a colon, and an English description of the value in errno Protocol Check whether a system/library call returns an error (usually -1 or sometimes NULL) If so, call perror (and possibly exit)
Perror... #include <stdio.h> #include <stdlib.h> #define FILENAME "example.dat" int main(void) { FILE *fp; fp = fopen(filename, "r"); if (fp == NULL) { perror("fopen"); exit(1); } fclose(fp); // should probably perror this too! return 0; }
Commandline Arguments int main(int argc, char *argv[]) { If we define main like that, then: argv[0] is the program name argv[1],..., argv[argc - 1] are the arguments argc is the number of arguments For anything more than simple filenames, use getopt to process arguments!
Commandline Arguments... #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { FILE *fp; if (argc!= 2) { fprintf(stderr, "Usage: %s filename\n", argv[0]); exit(1); } if ((fp = fopen(argv[1], "r")) == NULL) { printf("%s cannot be opened.\n", argv[1]); exit(1); } printf("%s can be opened.\n", argv[1]); return 0; }
Block I/O (King 22.6) //Read num_elem elements of size elem_size into ptr size_t fread(void *ptr, size_t elem_size, size_t num_elem, FILE *stream); //Write num_elem elements of size elem_size to file size_t fwrite(const void *ptr, size_t elem_size, size_t num_elem, FILE *stream); fread and fwrite can be used to read and write binary data They re often used to read from a file into an array or write an array to a file They return the number of elements (not bytes!) read or written
Example: Block I/O #include <stdio.h> #include <stdlib.h> int main(void) { short nums[] = {5, 200, 1000}; FILE *fp; if ((fp = fopen("example.dat", "w")) == NULL) { perror("fopen"); exit(1); } fwrite(nums, sizeof(short), 3, fp); fclose(fp); return 0; }
Types of Buffering (King 22.2) Unbuffered: output appears immediately (e.g. stderr) Line buffered: output appears when a whole line has been written (e.g. stdout when it is the screen) Block buffered: output appears when the buffer is filled (e.g. files) Use stderr for your fprintf debugging or your code will crash before you see your output!
Automatic Memory Allocation Suppose we want to write a convenience function that allocates and initializes an array of integers We tell it how many integers to allocate, and it returns a pointer to the beginning of the array int *nums; nums = make_array(10); // now nums is an array of 10 0 s
Automatic Memory Allocation... Here is an attempt at writing this function. int *make_array(int num_elements) { int array[num_elements]; int i; for (i = 0; i < num_elements; i++) array[i] = 0; return array; }
Automatic Memory Allocation... The problem is that array goes out of scope when the function returns The memory for array exists only while make_array is running, after which that memory is reclaimed What we have to be able to do is allocate memory that will not be lost when make_array finishes In other words, we require manual control of memory allocation
Dynamic Memory Allocation (King Ch 17) There s one other problem we have to allocate new memory each time the function is called to store the new array But when the program is being compiled, we can t possibly know how many times make_array will be called Therefore, we do not know how much memory to reserve at compile-time We must dynamically allocate memory while the program is running
Malloc void *malloc (size_t size); We use malloc to dynamically allocate memory malloc returns a pointer to the newly acquired memory, or NULL if there is not enough available memory To allocate memory for an array of n integers, we ask malloc for n times the size of an integer... int *nums; nums = malloc (n * sizeof(int));
Malloc... int *nums; nums = malloc (n * sizeof(int)); nums is now a pointer to an uninitialized array of n integers We can use it like any array nums[0] = 90; nums[1] = 50; nums 90 50...
A Correct make array Function int *make_array(int num_elements) { int *array = malloc(num_elements * sizeof(int)); int i; for (i = 0; i < num_elements; i++) array[i] = 0; return array; }
Freeing Memory Once we use malloc to ask for memory, we re responsible for releasing it when it s no longer required When we don t use malloc, memory is freed automatically when it goes out of scope So, when we re finished with the array, we should release its memory If we keep calling malloc without freeing any of the memory, memory will eventually run out of memory free (nums);
Lost Memory p = malloc(...); q = malloc(...); p q block1 block2 p = q; p q block1 block2 Since p now points to q s block, we have no way of accessing p s old block This is called a memory leak
Dangling Pointers int *p = malloc(5 * sizeof(int));... free(p); p is now a pointer to memory it does not own; it is an error to access memory through p p[0] = 0; // trouble p 90 50...
Java Comparison In Java: Set s; //Memory allocated for pointer s s = new HashSet(); //Memory allocated for a HashSet object In C: int *a; //Memory allocated for pointer a a = malloc(10 * sizeof(int)); //Memory for 10 ints