I am probably misunderstanding the question, but ...
Yes, you are.
If C had the feature that was being referred to originally --
denoted below by explicit "magic" -- one might write something like
this:
/* T_adder is a pointer to a function that takes one int argument
(say "i") and returns i+k for some constant k, but k is not
determined until later. */
typedef int (*T_adder)(int);
static int doit(int);
/* new_adder makes a new adder that adds k */
T_adder new_adder(int k) {
/* this is the part that doesn't work: */
T_adder ret;
ret = doit;
magic(ret)->saved_val = k; /* but there is no magic! */
return ret;
}
static int doit(int i) {
magic int k = magic()->saved_val; /* magically recover the saved val */
return i + k;
}
With the above in a module (say adder.c, and adder.h for the one
"typedef" line), we could then write, as our entire main() program:
#include <stdio.h>
#include <stdlib.h>
#include "adder.h"
int main(int argc, char **argv) {
T_adder a1, a2, a3;
int x;
a1 = new_adder(1); /* now a1(x) returns x+1 */
a2 = new_adder(99); /* and a2(x) returns x+99 */
if (argc >= 2)
a3 = new_adder(atoi(argv[1]));
else
a3 = new_adder(0); /* a3(x) returns x+k or x */
if (argc >= 3)
x = atoi(argv[2]);
else
x = 42;
printf("a1(%d) = %d\n", x, a1(x));
printf("a2(%d) = %d\n", x, a2(x));
printf("a3(%d) = %d\n", x, a3(x));
return EXIT_SUCCESS;
}
When run with no parameters, the output would be:
a1(42) = 43
a2(42) = 141
a3(42) = 42
and when run with one or two parameters the output would change.
The "magic" that is missing in C, but does exist in other languages,
is something that those languages usually call a "closure". A
closure allows you to capture the values of local variables and
save them away, then have the magically "resurrect" later. C has
no magic, so if you want to save away one or more values, you
have to do so explicitly -- and then the fact that there are saved
values becomes clear to the caller. We might rewrite adder.h as
follows:
typedef struct adder T_adder;
struct adder {
int k;
int (*run)(T_adder *, int);
};
Now adder.c looks like this:
static int doit(T_adder *, int);
T_adder *new_adder(int k) {
T_adder *ret;
ret = malloc(sizeof *ret);
if (ret == NULL)
panic("out of memory in new_adder()");
ret->k = k;
ret->run = doit;
return ret;
}
static int doit(T_adder *closure, int i) {
return closure->k + i;
}
and of course main.c has to change:
int main(int argc, char **argv) {
T_adder a1, a2, a3;
a1 = new_adder(1); /* now a1->run(a1, x) returns x+1 */
...
printf("a1(%d) = %d\n", x, a1->run(a1, x));
printf("a2(%d) = %d\n", x, a2->run(a2, x));
printf("a3(%d) = %d\n", x, a3->run(a3, x));
return EXIT_SUCCESS;
}
The magic has now been removed: the closure is explicitly available
in each "new_adder"-created adder, and when one calls the functions
associated with that adder (in this case just the one function named
"run"), one has to provide the environment holding that closure.
Those familiar with C++ should now comprehend how to fake closures
in C++.