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.