CSC 101 Lecture Notes Week 9
Pointers and Dynamic Memory Allocation
More on Programming in the Large
Relevant Reading: Chapter 14 Sections 14.1 and
14.2; Chapter 13 (again)
(type) malloc(sizeof(type))
int* nump; char* str; Planet* p; nump = (int*) malloc(sizeof int); str = (char*) malloc(sizeof char); p = (Planet*) malloc(sizeof Planet);
(type)calloc(sizeof(n, type))
/**** * * This program illustrates the dynamic allocation of string memory. It uses * malloc to allocate strings of a proper size. It uses calloc to allocate an * array big enough to hold the number of strings that the program reads in. * * The program takes two command-line arguments: the number of names to be read * and the file from which to read the names. Each line of the file is * considered to be a separate name. The length of a name can be up to 80 * characters. Any name longer than 80 characters is truncated to 80 * characters. Avoiding this truncation would require additional logic that * would overly complicate this example. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME_LEN 81 int main(int argc, char** argv) { int num_names; /* Number of names to read */ char* filename; /* Name of file to read from */ FILE* file; /* File to read from */ int i; /* Loop index variable */ char name[MAX_NAME_LEN]; /* One name read from the file */ char* name_sized; /* Dynamically sized name string */ char** names; /* Array of all names read from the file */ char* fstatus; /* return value of fgets */ /* * Check that there are two command-line args. */ if (argc < 2) { printf("The program requires two arguments -- number and file.\n"); return 1; } /* * Get the number of names to read and the name of the file to read from. * These are supplied by the program user in the first and second * command-line arguments. Since argv is a string array, we need to call * atoi on argv[1], to convert it from a string to an int. The filename in * argv[2] is good as a string. This kind of command-line arg processing * was done in lab 7. */ num_names = atoi(argv[1]); filename = argv[2]; /* * Open the file, exiting of not openable. */ if ((file = fopen(filename, "r")) == NULL) { printf("Cannot open file %s.\n", filename); return 1; } /* * Allocate a string array big enough to hold the number of names to be * read from the file. This allocation is one of the main ideas of this * example. It allows us to use an array of exactly the right size, rather * than declaring an array that tries to be big enough for any * eventuality. */ names = (char**) calloc(num_names, sizeof(char*)); /* * Read names from file, up to num_names or EOF, whichever occurs first. */ for (i = 0, fstatus = fgets(name, MAX_NAME_LEN, file); i < num_names && fstatus != NULL; i++, fstatus = fgets(name, MAX_NAME_LEN, file)) { /* * Allocate a string just the right size for name. This allocation is * the other main idea of this example. It allows us to allocate * string storage that's exactly the size we need. What we avoid is * having every name stored in a string of size MAX_NAME_LEN, when that * would be quite wasteful of space in most cases. */ name_sized = (char*) malloc(strlen(name + 1)); strcpy(name_sized, name); /* * Store the sized name in the names array. */ names[i] = name_sized; } /* * Update the value of num_names to be the actual number of names read in, * in case it's different than the command-line arg value. */ num_names = i; /* * Print out array to confirm that names were properly read. */ for (i = 0; i < num_names; i++) { printf("%s", names[i]); } return 0; }
/**** * * This is a version of the read-names program that does not use dynamic memory * allocation. It's provided for comparision purpose, to illustrate the * difference between static and dynamic allocation of arrays. * * The program takes two command-line arguments: the number of names to be read * and the file from which to read the names. Each line of the file is * considered to be a separate name. The length of a name can be up to 80 * characters. Any name longer than 80 characters is truncated to 80 * characters. Avoiding this truncation would require additional logic that * would overly complicate this example. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME_LEN 81 #define MAX_NAMES 1000 int main(int argc, char** argv) { int num_names; /* Number of names to read */ char* filename; /* Name of file to read from */ FILE* file; /* File to read from */ int i; /* Loop index variable */ char name[MAX_NAME_LEN]; /* One name read from the file */ char names[MAX_NAMES][MAX_NAME_LEN]; /* Array of all names read from the file */ char* fstatus; /* return value of fgets */ /* * Check that there are two command-line args. */ if (argc < 2) { printf("The program requires two arguments -- number and file.\n"); return 1; } /* * Get the number of names to read and the name of the file to read from. * These are supplied by the program user in the first and second * command-line arguments. Since argv is a string array, we need to call * atoi on argv[1], to convert it from a string to an int. The filename in * argv[2] is good as a string. This kind of command-line arg processing * was done in lab 7. */ num_names = atoi(argv[1]); filename = argv[2]; /* * Open the file, exiting of not openable. */ if ((file = fopen(filename, "r")) == NULL) { printf("Cannot open file %s.\n", filename); return 1; } /* * Check that the number of names to be read is <= MAX_NAMES. If it's not, * set it to MAX_NAMES. Compare this code to the use of calloc in * read-names.c. */ if (num_names >= MAX_NAMES) { printf("Of the %d names requested, only the first %d will be read.\n", num_names, MAX_NAMES); num_names = MAX_NAMES; } /* * Read names from file, up to num_names or EOF, whichever occurs first. */ for (i = 0, fstatus = fgets(name, MAX_NAME_LEN, file); i < num_names && fstatus != NULL; i++, fstatus = fgets(name, MAX_NAME_LEN, file)) { /* * Copy the name just read into the ith array element. Compare this * code to the use of malloc in read-names.c. */ strcpy(names[i], name); } /* * Update the value of num_names to be the actual number of names read in, * in case it's different than the command-line arg value. */ num_names = i; /* * Print out array to confirm that names were properly read. */ for (i = 0; i < num_names; i++) { printf("%s", names[i]); } return 0; }
Able Jones Smith Wallace
Figure 1: Dynamically allocated memory.
Figure 2: Statically allocated memory.