Let's try something else. I have a lot of experience in programming but I
have written about 10 lines of C (and no Java, C++ or anything related) and
not had to work with it or close to it ever.
Does this code fragment mean
1. allocate some storage
2. free it
3. assign a value to the area pointed to by the pointer to the storage you just
freed
If so that won't work forever, especially not in a multiuser or
multiprocessing environment.
In practical terms (how this typically works): the freed object's memory area
is no longer owned by the program, but by the memory allocator. The memory
allocator can put bits there, such as link pointers that put the object on a
free list. If that is how the allocator works, it will typically put those
bits there right away, before the free function returns.
In many memory allocators, in the interests of saving space, allocated objects
do not have any headers. This allows objects to be adjacently allocated with
no wasted space. Only free objects are kept on free lists, and the bookkeeping
pieces such as pointer and flag fields for keeping free lists go inside
the objects, in the same memory where the application's data used to be
when those objects were allocated.
After the program did the above, it might continue executing fine until the
next time it makes any kind of call to the memory allocator. This is true even
if there is no concurrency. The memory allocator might walk its free lists
and hit a bad pointer due to the clobbered memory location.
In ``ISO C language lawyer'' terms, as soon sa free(f) is called, the pointer f
(and all copies of f that the program may have elsewhere) become
``indeterminate'' values. The use of an indeterminately-valued object results
in undefined behavior.
I.e undefined behavior occurs in the expression f->x itself, regardless of the
assignment to that location f->x = 1. The expression f->x uses the pointer f
(to access the place x, or to designate it as an lvalue). This use is undefined
since f is indeterminate.
It might work for a long time on a single user
DOS though.
Perhaps, if the program does not ever make another call to functions like
malloc, free, realloc, calloc again.
If the DOS memory allocator is being directly used by malloc, this might screw
up the operating system.
If I got it right you don't need 3-5 years of programming in C to realize
the problem you just need to have a clue.
If I am wrong then I will probably
need 3-5 years of programming in C to understand the issue!
Btw I don't understand the nuances of the first line, does it mean define a
The snippet is incomplete. The first line assumes that there is a 'struct foo'
type that has been previously defined, which has a member x of integer type (or
some arithmetic type to which 1 can be assigned). The first line could define
the type by including the body of the structure:
struct foo { int x; } *f = malloc(sizeof *f);
This is a declaration with an initializer. The 'struct foo .. { ... }'
is the list of declaration specifiers (containing one specifier: that
for a structure type). The *f part is a declarator, declaring a name f.
The * type construction operator means that f is declared as a pointer
type (a pointer to what? The type produced by the declaration specifier list).
C declarations are split into specifiers and declarators, which allows
multiple declarators to hang off a shared "stem" of specifiers, and
yet declare different kinds of things:
/* x is of type int; y is a pointer to int;
z is a function of no arguments returning int. */
int x, *y, (*z)(void);
Our pointer f is initialized from the return value of a call to the principal C
memory allocation function, malloc. sizeof is an operator for computing the
sizes of types based on object-designating expressions or type expressions, and
*f is an object-designating expression which means "the object obtained by
dereferencing the pointer f". f is not yet initialized, so the object does not
exist, but that doesn't matter because sizeof doesn't use an expression's
value, only its type. The type of *f is "struct foo". So we are asking malloc
for enough bytes to cover the structure.
If you write some_type *p = malloc(sizeof *p), then the size automatically
adjusts itself if you edit something to other_type: you have not
mentioned the type name in two places, as in:
some_type *p = malloc(sizeof (some_type)); /* more error-prone */