Page 189

The standard library function calloc(n,size) returns a pointer to n objects of size size, with the storage initialized to zero. Write calloc, by calling malloc or by modifying it.

/* I found this SO answer very helpful in understanding some of the routines presented:
 * https://stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book/36512105#36512105 */

#include <unistd.h>

typedef long Align;

union header {
	struct {
		union header *ptr;
		unsigned size;
	} s;
	Align _;
};

typedef union header Header;

static Header base;
static Header *freep = NULL;
static Header *knrmorecore(unsigned nu);

void *knrmalloc(unsigned nbytes)
{
	Header *currp, *prevp;
	Header *morecore(unsigned);
	unsigned nunits;

	nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;

	if ((prevp = freep) == NULL) {
		base.s.ptr = freep = prevp = &base;
		base.s.size = 0;
	}

	for (currp = prevp->s.ptr;; prevp = currp, currp = currp->s.ptr) {
		if (currp->s.size >= nunits) {
			if (currp->s.size == nunits) {
				prevp->s.ptr = currp->s.ptr;
			} else { /* allocate tail end */
				currp->s.size -= nunits;
				currp += currp->s.size;
				currp->s.size = nunits;
			}
			freep = prevp;
			return (void *)(currp + 1);
		}
		if (currp == freep) {
			if ((currp = knrmorecore(nunits)) == NULL) {
				return NULL;
			}
		}
	}
}

#define NALLOC 1024
void knrfree(void *ap);
static Header *knrmorecore(unsigned nu)
{
	char *cp;
	Header *up;

	if (nu < NALLOC) {
		nu = NALLOC;
	}
	cp = sbrk(nu * sizeof(Header));
	if (cp == (char *)-1) {
		return NULL;
	}
	up = (Header *)cp;
	up->s.size = nu;
	knrfree((void *)(up + 1));
	return freep;
}

void knrfree(void *ap)
{
	Header *insertp, *currp;

	insertp = (Header *)ap - 1;
	for (currp = freep; !(insertp > currp && insertp < currp->s.ptr);
	     currp = currp->s.ptr) {
		if (currp >= currp->s.ptr &&
		    (insertp > currp || insertp < currp->s.ptr)) {
			break;
		}
	}

	if (insertp + insertp->s.size == currp->s.ptr) { /* join to upper nbr */
		insertp->s.size += currp->s.ptr->s.size;
		insertp->s.ptr = currp->s.ptr->s.ptr;
	} else {
		insertp->s.ptr = currp->s.ptr;
	}

	if (currp + currp->s.size == insertp) {
		currp->s.size += insertp->s.size;
		currp->s.ptr = insertp->s.ptr;
	} else {
		currp->s.ptr = insertp;
	}
	freep = currp;
}

#include <string.h>
void *mycalloc(int n, size_t size)
{
	void *res = knrmalloc(n * size);
	if (res != NULL) {
		memset(res, 0, n * size);
	}
	return res;
}

#include <stdio.h>
#include <stdlib.h>
int main()
{
	char *str = (char *)knrmalloc(100 * sizeof(char));
	if (str == NULL) {
		puts("error: knrmalloc failed");
		exit(1);
	}

	strcpy(str, "hello, world!");
	puts(str);

	str = (char *)mycalloc(100, sizeof(char));
	if (str == NULL) {
		puts("error: mycalloc failed");
		exit(1);
	}

	strcpy(str, "hello, world!");
	puts(str);
}