Memory and utilities (stdlib.h)¶
Include stdlib.h. This header covers dynamic memory,
string-to-number conversion, integer arithmetic helpers, searching and sorting,
process control, and pseudo-random numbers.
Types and Macros¶
| Name | Meaning |
|---|---|
NULL |
Null pointer constant. |
EXIT_SUCCESS |
Successful program termination status. |
EXIT_FAILURE |
Unsuccessful program termination status. |
RAND_MAX |
Maximum value returned by rand. |
ATEXIT_MAX |
Maximum number of functions that can be registered with atexit(). |
div_t |
Quotient and remainder pair returned by div. |
ldiv_t |
Quotient and remainder pair returned by ldiv. |
MB_CUR_MAX |
Maximum bytes in a multibyte character in the current locale. |
Functions¶
| Function | Summary |
|---|---|
_Noreturn void abort( void ) |
Terminate the program abnormally; does not call atexit handlers. |
int abs(int j) |
Absolute value of a signed int. |
int atexit( void (*func)(void) ) |
Register func to be called at normal program termination (LIFO order). Returns 0 on success, nonzero if the ATEXIT_MAX table is full. |
float atof(const char *nptr) |
Convert the leading decimal text in nptr to float. Note: C89 atof normally returns double; dcc has no double type, so this returns float (IEEE 754 single precision). Accepts nan, inf, and infinity spellings. Overflow returns signed infinity; underflow returns signed zero. |
int atoi(const char *nptr) |
Convert leading decimal text in nptr to int after skipping C whitespace. |
long atol(const char *nptr) |
Convert leading decimal text in nptr to long after skipping C whitespace. |
int bdos( int fn, int dearg ) |
Call the CP/M BDOS entry point. |
int bdoshl( int fn, int dearg ) |
Call the CP/M BDOS entry point, returning the full HL result. |
int bios( int fn, int dearg ) |
Call the CP/M BIOS jump table directly. |
int bioshl( int fn, int dearg ) |
Call the CP/M BIOS jump table directly, returning the full HL result. |
int biosreg( int fn, int bcarg, int dearg ) |
Call the CP/M BIOS with independent BC and DE arguments, returning HL. |
void *bsearch(const void *key, const void *base, size_t num, size_t size, int (*compare)(const void *, const void *)) |
Binary-search a sorted array. |
void *calloc( size_t num, size_t size ) |
Allocate and zero num * size bytes from the heap. |
div_t div(int numer, int denom) |
Signed int division returning quotient and remainder. |
int exec( const char *path, const char *cmdtail ) |
Load and run path, replacing this process. cmdtail is copied through its first NUL or CR into private staging before loader side effects, then to 0x81 (e.g. " ARG1 ARG2"). At most 127 text bytes are accepted; a trailing CR is written when space remains. The first two arguments, delimited by bytes through ASCII space, also seed the default FCBs at 0x5C and 0x6C. path must be an unambiguous CP/M 8.3 filename with an optional A: through P: drive prefix; ".COM" is appended when no extension is present. Returns -1 with errno=E2BIG for an oversized tail, EINVAL for an invalid path, ENOENT when the file cannot be opened, or EFBIG when its 128-byte-record-rounded image cannot fit safely in the TPA; does not return on success. |
int execv( const char *path, char **argv ) |
Like exec() but builds the command tail from argv[1..] (argv[0] is the conventional program name and is ignored for CP/M purposes). argv must be a NULL-terminated array of string pointers. Because DCC's direct CP/M startup parser deliberately has no quoting/escape syntax, each argument must be nonempty and contain no byte through ASCII space; quote and backslash are ordinary bytes. Returns -1 with errno=EINVAL when an argument cannot round-trip, or errno=E2BIG above the 127-byte tail limit. |
_Noreturn void exit( int code ) |
Terminate the program after flushing runtime output. |
void free( void *ptr ) |
Release a heap allocation. |
char *getenv(const char *name) |
Search the environment for name; always returns NULL on CP/M 2.2. |
int inp( unsigned port ) |
Read an 8-bit Z80 I/O port. |
long labs(long j) |
Absolute value of a signed long. |
ldiv_t ldiv(long numer, long denom) |
Signed long division returning quotient and remainder. |
void *malloc( size_t size ) |
Allocate size bytes from the heap. |
int mblen(const char *s, size_t n) |
Length of the multibyte character at s, examining at most n bytes. |
size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n) |
Convert at most n multibyte characters from s into the wchar_t array pwcs. |
int mbtowc(wchar_t *pwc, const char *s, size_t n) |
Convert the multibyte character at s into *pwc; examine at most n bytes. |
void outp( unsigned port, unsigned val ) |
Write an 8-bit Z80 I/O port. |
void qsort(void *base, size_t num, size_t size, int (*compare)(const void *, const void *)) |
Sort an array in place. |
int rand(void) |
Return the next pseudo-random integer in the range 0 through RAND_MAX. |
void *realloc( void *ptr, size_t size ) |
Resize a heap allocation, preserving contents up to the smaller size. |
void srand(unsigned int seed) |
Seed the pseudo-random number generator. |
float strtod(const char *nptr, char **endptr) |
Convert leading floating-point text in nptr to float; sets *endptr past consumed input. Note: C89 strtod returns double; dcc returns float (no double type). Numeric overflow returns signed infinity and underflow returns signed zero; both set errno to ERANGE. Explicit inf/nan spellings are not range errors. |
long strtol(const char *nptr, char **endptr, int base) |
Convert text in nptr to long using base 2 through 36, or base 0 for auto-detection. |
unsigned long strtoul(const char *nptr, char **endptr, int base) |
Convert text in nptr to unsigned long using base 2 through 36, or base 0 for auto-detection. |
int system(const char *string) |
Execute a shell command. CP/M 2.2 has no command processor: system(NULL) correctly reports that via 0 (false), and any non-NULL command returns -1 (unsupported). |
size_t wcstombs(char *s, const wchar_t *pwcs, size_t n) |
Convert at most n bytes from pwcs into s; return (size_t)-1 on an unrepresentable wide character. |
int wctomb(char *s, wchar_t wc) |
Convert wc into one byte at s, or return -1 if wc is unrepresentable. |
Runtime model¶
The standard functions in this header are runtime-backed. DCC C Compiler also declares a small set of CP/M and Z80 extensions here (BDOS/BIOS wrappers, port I/O, and program replacement); those are documented with the CP/M services rather than treated as portable C APIs.
Dynamic memory¶
The allocator uses a first-fit heap walk with two-byte packed boundary tags at
the start and end of each block. Freeing a block coalesces it with adjacent free
neighbours (including blocks freed via realloc(p, 0) and the old block
released by a growing realloc), which keeps fragmentation down. realloc
also grows in place at the heap top or into an immediately following free
block. The heap grows on demand between the end of BSS and the stack. On CP/M
this space is bounded by the program's TPA: code, data, runtime support, heap,
and stack all share the same transient program area.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *buffer = malloc(256);
char *resized;
if (!buffer)
return EXIT_FAILURE;
resized = realloc(buffer, 512);
if (!resized) {
free(buffer);
return EXIT_FAILURE;
}
buffer = resized;
free(buffer);
return EXIT_SUCCESS;
}
realloc follows the standard rules: realloc(NULL, n) behaves like
malloc(n), and realloc(p, 0) frees p and returns NULL.
Allocation failure returns NULL; a failed nonzero realloc leaves the
original block valid. Do not overwrite your only pointer before checking the
result. free(NULL) does nothing. Zero-byte malloc requests use the minimum
allocation size and can still fail. calloc rejects a product that exceeds
16-bit size_t; check multiplication before calling malloc(count * size),
where the expression itself can already have wrapped. Allocation failure does
not guarantee that errno is set.
Size cost
calloc uses checked size multiplication and zero-filling in addition to
allocation. strdup also requires the allocator. See the
appendix.
Conversion¶
atoi/atol skip the full C whitespace set (space and bytes \t through
\r), accept an optional +/- sign, then consume decimal digits; conversion
stops at the first non-digit. Overflow wraps modulo the type width.
int n = atoi(" -123xyz"); /* -123 */
long m = atol(" -123456"); /* -123456L */
strtol/strtoul are the full C89 conversions. They skip leading whitespace,
accept an optional sign, honour a 0x/0X prefix for base 16 and a leading 0
for base 8 when base is 0, and accept digits/letters up to base-1 for any
base from 2 to 36. The unused tail is reported through *end when end is
non-NULL. On overflow they clamp to LONG_MAX/LONG_MIN (or ULONG_MAX) and
set errno to ERANGE. A 0x prefix is recognized only when a hexadecimal
digit follows it; for "0x" or "0xG" the leading zero is converted and
end points at the x, without changing errno.
char *end;
long v = strtol(" -0x1Ag", &end, 0); /* v = -26, *end = 'g' */
unsigned long u = strtoul("4294967295", NULL, 10); /* ULONG_MAX */
atof is available as a DCC C Compiler extension: it is declared as float atof(const char *nptr)
and returns IEEE 754 single precision. C89 atof normally returns double, which DCC C Compiler
does not have. It accepts ordinary decimal text with an optional exponent, plus the case-insensitive
spellings nan, inf, and infinity. Overflow returns signed infinity; underflow returns signed zero.
strtod uses the same parser and reports the first unconsumed byte through
endptr. Numeric overflow and underflow set errno to ERANGE; explicit
infinity/NaN spellings and an exact zero with a large exponent are not range
errors.
For checked input, prefer strtol/strtoul/strtod to atoi/atol/atof.
Set errno = 0 before conversion, check whether endptr advanced, inspect any
unconsumed suffix, and check ERANGE. A zero result alone cannot distinguish
valid zero from failed conversion.
Multibyte and wide characters¶
DCC uses a fixed single-byte execution encoding (MB_CUR_MAX == 1).
Byte values 0x00 through 0xFF map to equal-valued 16-bit wchar_t values.
A wider value is unrepresentable: wctomb returns -1 without writing a
truncated byte, and wcstombs returns (size_t)-1 at the offending element.
wcstombs may already have stored a representable prefix, as permitted by C,
but never stores the truncated offending value. With n == 0, it examines and
writes no elements and returns zero.
Integer arithmetic helpers¶
div returns a div_t with quot and rem members; ldiv returns an
ldiv_t with 32-bit members. Signed division truncates toward zero; the
remainder has the same sign as the numerator.
div_t d = div(-7, 3); /* d.quot == -2, d.rem == -1 */
ldiv_t ld = ldiv(200000L, 7L);
Searching and sorting¶
Both take the standard comparator: cmp(a, b) returns negative if a sorts
before b, zero if equal, positive if after. qsort uses an in-place,
non-recursive Shell sort, so it is not stable; bsearch requires the array
to be sorted by the same comparator. See Worked examples for
complete programs.
Process control¶
exit(status) and returning from main run registered atexit functions in
reverse registration order, clean up temporary files, and flush console output.
atexit returns zero on success and nonzero when its 32-entry table is full.
Close ordinary files explicitly before exiting. abort flushes console output
and warm-boots without running handlers or temporary-file cleanup; it does not
set an exit status.
CP/M has no environment-variable service: getenv always returns NULL.
There is no supported returning shell-command service: system(NULL) returns
zero and system(command) returns -1. Use the DCC-specific exec functions
below only when replacing the current program is intended.
The exit code is surfaced through CP/M 3.0 BDOS call 108, which emulators such
as ntvcm reflect in their own process exit code. Returning a value from main
has the same effect.
exec() and execv() replace the current program through the CP/M command-tail
area. DCC accepts the full 127-byte payload in 0x81..0xFF; the length byte is
authoritative, so the conventional trailing CR is omitted only at that exact
maximum. A 128th byte returns -1 with errno == E2BIG. exec() validates
and copies its caller-owned source into private staging before opening the
image, abandoning the caller stack, clearing FCBs, or writing the destination
tail, so stack-local and overlapping low-memory sources are safe.
The executable path must be an unambiguous CP/M 8.3 name: an optional
A: through P: drive, one to eight filename bytes, and an optional one to
three byte filetype. DCC appends .COM when the filetype is absent. Invalid
syntax returns EINVAL, a failed open returns ENOENT, and an executable whose
128-byte-record-rounded image cannot fit below the loader's reserved high-memory
stack/FCB/trampoline ranges returns EFBIG.
The BDOS function 35 record count is also the loader's exact read contract. The high-memory trampoline performs exactly that many successful sequential reads, never reads a newly grown extra record, and warm-boots rather than jumping to a partial image if any approved read returns a nonzero status.
The startup parser intentionally keeps direct CP/M behavior: bytes through
ASCII space delimit arguments, while quote and backslash are literal bytes and
have no escaping role. Therefore execv() can round-trip only nonempty
arguments containing bytes above ASCII space. It returns -1 with
errno == EINVAL for empty arguments or arguments containing spaces, tabs, or
other delimiter bytes rather than silently changing argv.
The first two command-tail words seed CP/M's default FCB1 and FCB2. Their delimiter rule is identical to startup argument parsing: every byte through ASCII space, including tab and control whitespace, is a delimiter.
Pseudo-random numbers¶
RAND_MAX is 32767.
srand(1);
int roll = rand() % 6 + 1; /* a die roll */
The runtime generator is a 16-bit xorshift with parameters 7, 9, and 8. The
state is 16-bit, srand(seed) stores the state directly, and rand() clears
bit 15 of the updated state so the result stays in the C89 0 .. RAND_MAX
range. In C-equivalent form:
static unsigned int s_rnd = 1;
void srand(unsigned int seed)
{
s_rnd = seed;
}
int rand(void)
{
s_rnd ^= s_rnd << 7;
s_rnd ^= s_rnd >> 9;
s_rnd ^= s_rnd << 8;
return (int)(s_rnd & 0x7fff);
}
That deterministic sequence is useful for benchmarks: if another CP/M compiler
uses the same C equivalent, tests that depend on rand() can compare runtime
library and code-generation performance without being skewed by different
pseudo-random sequences.