Text | Contains the compiled code of the main program,
its subroutines, and any static libraries. |
Fixed size |
Data | Often considered to have three parts: (1) The data area contains initialized variables with static duration. (2) The BSS area contains uninitialized variables with static duration. (3) The heap contains data allocated and freed explicitly by the running program. | Data and BSS areas are fixed size. Heap can grow and shrink. |
Stack | Contains variables of automatic duration. | Grows with function calls. Shrinks with function returns. |
void *
malloc(NumberOfBytes)
calloc(NumberToAllocate, SizeOfEach)
free(MemoryAddress)
#include <stdio.h> #include <stdlib.h> #include <malloc.h> int main(int argc, char** argv) { int numberToReverse ; printf("How many to reverse?\n") ; while (scanf("%d", &numberToReverse)==1 && numberToReverse > 0) { int i ; int *ReverseThese ; if ((ReverseThese=(int *)malloc(numberToReverse*sizeof(int))) == NULL) { printf("Too big\n") ; return(EXIT_FAILURE) ; } for(i=0; i < numberToReverse; ++i) { printf("Enter number %4d\n", i) ; scanf("%d", &ReverseThese[i]) ; } for(i=numberToReverse-1; i >= 0; --i) { printf("Number %4d: %8d\n", i, ReverseThese[i]) ; } free(ReverseThese) ; } return (EXIT_SUCCESS); }
NULL
return
if ((p=malloc(10000)) == NULL) { fputs("Out of memory\n", stderr) ; exit(EXIT_FAILURE) ; }
void *
as needed
char *p ; if ((p=(char *)malloc(10000)) == NULL) { ................ ................ free((void *)p) ;
sizeof
for safety and portability
int *p ; ................ if ((p=(int *)malloc(10000 * sizeof(int))) == NULL) { ................ if ((p=(int *)malloc(10000 * sizeof *p)) == NULL) { ................ if ((p=(int *)calloc(10000, sizeof(int))) == NULL) {
int *p, *q ; .... if ((p=(int *)calloc(10, sizeof(int))) == NULL) { .... q = p ; .... free(p) ; /* q becomes a dangling pointer */ .... printf("%d\n", *q) ;
int DoSomething(int a) { int *p ; .... if ((p=(int *)calloc(10, sizeof(int))) == NULL) { .... /* p is lost memory */ return 5 ; }
BigObject p = new BigObject(209, 2009) ; ................ delete p ;
BigObject p = new BigObject(209, 2009) ;
C has three type qualifiers whose most like use will be with
pointers.
The qualifiers may be attached to a declared variable or to
lvalues based on that variable. For example, if
p
is declared as a pointer to an integer, then
the qualifier may be attached to either p
or
*p
. For example, the following declarations
prevent assignments to p
and *q
.
However, assignments may be made to *p
and
q
.
int * const p ; const int * q ;
The const
qualifier was added in the C89 standard.
The const
qualifier insures that no modifications are
made to a variable through a C lvalue.
In some instances declaring a variable as const
may allow
a compiler to generate more efficient code, but the main use of
const
is to declare useful program constants or to
indicate that a procedure will not modify data accessed through
a passed pointer.
The following prototype declaration assures us that strncpy
will
not use the source
parameter to modify the string referenced
by source
.
char *strncpy(char *destination, const char *source, size_t n) ;
However, if you pass overlapping pointers to strcpy
,
all bets are off.
The volatile
qualifier was also added in the C89 standard.
The volatile
qualifier is rarely seen in the application code.
It is used to indicate that an lvalue can be changed by some
outside agent, such as a device or a sibling
thread,
a concurrently executing C program sharing a common set of variables.
The volatile
qualifier prevents a compiler from
making otherwise sensible optimizations. For example, in the
following sequence of code, where p
is declared as
a pointer to an integer,
it seems obvious that x
will be assigned the value 5 and the "smart" compiler should just
generate code that assigns 5 to x
without
dereferencing p
.
*p = 5; /* Do nothing for a million increments of i */ for (int i=0; i<1000000; ++i) { } x = *p ;
However, if *p
was defined a volatile
,
the compiler would know that the value of *p
could
be changed by something other than the for
between
the writing and reading of *p
and would
generate machine code to dereference p
after the loop.
The volatile
declarations
are needed in the following code because they refer to data
that can be modified by memory-mapped I/O devices.
/* Based on the Keyboard Echo example from Patt and Patel (p. 207) */ volatile int * KBSR = (int *) 0xFE00 ; volatile int * KBDR = (int *) 0xFE02 ; volatile int * DSR = (int *) 0xFE04 ; volatile int * DDR = (int *) 0xFE06 ; int NewChar ; while (*KBSR > 0) /*spin*/ ; NewChar = *KBDR ; while (*DSR > 0) /*spin*/ ; *DDR = NewChar ;
The restrict
qualifier wasn't added to C until the
C99 standard.
The restrict
qualifier is very difficult to explain
(and understand)
and can only be applied to pointers, but the basic idea is that
the restrict
ed pointer does not "share" referenced
"objects" with other pointers.
The most common use of restrict
is with function parameters.
Consider the following C function with restrict
ed
parameters.
void multiplyVec(double * restrict X, double * restrict Y, int n) { *X = *X * n ; *Y = *Y * n ; }
Normally, the C compiler could not schedule the two
multiplications to be preformed concurrently,
because X
and Y
might point to the same double
.
However, with restrict
the compiler knows that
there is no sharing between the pointers, and it can generate faster
code.
However, don't expect the compiler to complain if someone
writes multiplyVec(&A, &A, 100)
.
Just to illustrate how difficult it is to enforce these rules on
calls consider calls to the strncpy
function.
char *strncpy(char * restrict dest, const char * restrict src, size_t n) ;
The purpose of the restrict
is to prevent the
two strings from overlapping. This means that the following call
confirms to the restrict
if i
is 3 or
less but doesn't otherwise. It is impossible for a compiler
to always determine if i
will satisfy this rule.
strncpy(&c[3], &c[0], i)