Companion code · Chapter 6

The Stack and the Heap: Dynamic Memory and Ownership

5 files. About this chapter.

Download ch06.zip

ch06/alloc_cost.c41 lines
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 1000000

int main(void) {
    clock_t t0 = clock();
    double **each = malloc(N * sizeof *each);   /* N separate blocks */
    if (each == NULL) {
        return 1;
    }
    for (size_t i = 0; i < N; i++) {
        each[i] = malloc(sizeof **each);
        if (each[i] == NULL) {
            while (i > 0) {
                free(each[--i]);
            }
            free(each);
            return 1;
        }
        *each[i] = 20.0;
    }
    for (size_t i = 0; i < N; i++) {
        free(each[i]);
    }
    free(each);
    clock_t t1 = clock();
    double *one = malloc(N * sizeof *one);      /* one block */
    if (one == NULL) {
        return 1;
    }
    for (size_t i = 0; i < N; i++) {
        one[i] = 20.0;
    }
    free(one);
    clock_t t2 = clock();
    printf("N separate blocks: %.1f ms\n", 1000.0 * (t1 - t0) / CLOCKS_PER_SEC);
    printf("one block:         %.1f ms\n", 1000.0 * (t2 - t1) / CLOCKS_PER_SEC);
    return 0;
}
ch06/batch.c40 lines
#include <stdio.h>
#include <stdlib.h>

#define LINE_CAP 64
/* Ownership: on success returns a block of *count doubles that the
 * CALLER owns. Returns NULL on bad input or if memory runs out. */
double *load_values(FILE *in, size_t max, size_t *count) {
    double *vals = malloc(max * sizeof *vals);
    char *line = malloc(LINE_CAP);
    if (vals == NULL || line == NULL) {
        free(vals);
        free(line);
        return NULL;
    }
    size_t n = 0;
    while (n < max && fgets(line, LINE_CAP, in) != NULL) {
        char *end;
        vals[n] = strtod(line, &end);
        if (end == line) {            /* not a number */
            fprintf(stderr, "bad line %zu\n", n + 1);
            free(line);
            return NULL;
        }
        n++;
    }
    free(line);
    *count = n;
    return vals;
}

int main(void) {
    size_t n = 0;
    double *vals = load_values(stdin, 1440, &n);   /* main owns */
    if (vals == NULL) {
        return 1;
    }
    printf("loaded %zu, first %.2f\n", n, n > 0 ? vals[0] : 0.0);
    free(vals);
    return 0;
}
ch06/batch_failk.c46 lines
#include <stdio.h>
#include <stdlib.h>

#define LINE_CAP 64
static long fail_at = 0, alloc_count = 0;
static void *stn_malloc(size_t size) {
    alloc_count++;
    return alloc_count == fail_at ? NULL : malloc(size);
}
/* Ownership: on success returns a block of *count doubles that the
 * CALLER owns. Returns NULL on bad input or if memory runs out. */
double *load_values(FILE *in, size_t max, size_t *count) {
    double *vals = stn_malloc(max * sizeof *vals);
    char *line = stn_malloc(LINE_CAP);
    if (vals == NULL || line == NULL) {
        free(vals);
        free(line);
        return NULL;
    }
    size_t n = 0;
    while (n < max && fgets(line, LINE_CAP, in) != NULL) {
        char *end;
        vals[n] = strtod(line, &end);
        if (end == line) {            /* not a number */
            fprintf(stderr, "bad line %zu\n", n + 1);
            free(line);
            return NULL;
        }
        n++;
    }
    free(line);
    *count = n;
    return vals;
}

int main(int argc, char **argv) {
    fail_at = argc > 1 ? strtol(argv[1], NULL, 10) : 0;
    size_t n = 0;
    double *vals = load_values(stdin, 1440, &n);   /* main owns */
    if (vals == NULL) {
        return 1;
    }
    printf("loaded %zu, first %.2f\n", n, n > 0 ? vals[0] : 0.0);
    free(vals);
    return 0;
}
ch06/heap_year.c33 lines
#include <stdio.h>
#include <stdlib.h>

#define YEAR_READINGS 2102400   /* 4 sensors x 525,600 minutes */

/* Ownership: returns a heap block of n doubles that the CALLER
 * owns and must free. Returns NULL if memory is not available. */
double *make_series(size_t n) {
    double *p = malloc(n * sizeof *p);
    if (p == NULL) {
        return NULL;
    }
    for (size_t i = 0; i < n; i++) {
        p[i] = 20.0 + (double)(i % 10) / 10.0;
    }
    return p;
}

int main(void) {
    double *year = make_series(YEAR_READINGS);  /* main owns */
    if (year == NULL) {
        fprintf(stderr, "out of memory\n");
        return 1;
    }
    double sum = 0.0;
    for (size_t i = 0; i < YEAR_READINGS; i++) {
        sum += year[i];
    }
    printf("mean = %.2f over %d readings\n", sum / YEAR_READINGS,
           YEAR_READINGS);
    free(year);                       /* the owner frees, once */
    return 0;
}
ch06/opening.c45 lines
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int cmp_double(const void *a, const void *b) {
    double x = *(const double *)a, y = *(const double *)b;
    return (x > y) - (x < y);
}

/* Writes the median of vals[0..n-1] to *out. Returns 0, or -1
 * if a reading is NaN (a faulty sensor). vals is borrowed. */
int stn_median(const double *vals, size_t n, double *out) {
    double *copy = malloc(n * sizeof *copy);
    if (copy == NULL) {
        return -1;
    }
    memcpy(copy, vals, n * sizeof *copy);
    for (size_t i = 0; i < n; i++) {
        if (isnan(copy[i])) {
            return -1;                 /* sensor fault */
        }
    }
    qsort(copy, n, sizeof *copy, cmp_double);
    *out = copy[n / 2];
    free(copy);
    return 0;
}

int main(int argc, char **argv) {
    double day[1440];
    for (size_t i = 0; i < 1440; i++) {
        day[i] = 15.0 + (double)(i % 60) / 10.0;
    }
    if (argc > 1 && strcmp(argv[1], "fault") == 0) {
        day[700] = NAN;                /* simulate a fault */
    }
    double m;
    if (stn_median(day, 1440, &m) == 0) {
        printf("median %.2f\n", m);
    } else {
        printf("sensor fault\n");
    }
    return 0;
}