/**** * * 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 #include #include #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; }