allocation of local variables on stack

C

Chris Torek

Russell Shaw said:
Sometimes you don't know how much data you're going to read, so you
just have to plonk stuff on the stack regardless:

char* getfile(FILE *infile)
{
size_t i = 0;
char myarray[0];

while(!feof(infile)) {
myarray[i++] = fgetc(infile);
}

alloca(i);

char *buf = malloc(i);
memcpy(buf, myarray, i);
return buf;
}

It is worse than that: it does not even work, crashing right after
reading sufficiently long lines (even when using gcc on Linux on
an i386).

Indeed.

How about:

char* getfile(FILE *infile)
{
size_t i = 0;

while(!feof(infile)) {
char *ptr = alloca(sizeof(char));
*ptr = fgetc(infile);
if(*ptr == EOF) {
return NULL;
}
}

char *buf = malloc(i);
memcpy(buf, ptr, i);
return buf;
}


This one has the advantage of "almost working" on some machines.

It still behaves very oddly on a SPARC. (Try it! It breaks even
on a SPARC running Linux, using gcc.)

The reason is that alloca() on the SPARC rounds the size up to
the nearest multiple of 8, as required by the architecture. Each
alloca(1) -- sizeof(char) is necessarily 1 -- allocates 8 bytes.
The text winds up strangely spaced.

It also fails when run on a few other, in another peculiar way: it
adds a mysterious '\377' character to the file. The reason is that
fgetc(infile) returns EOF, which is stored in *ptr, which has
type (plain) char, which is unsigned char. The test *ptr==EOF
compares 255 against -1; they are not equal and we make one more
trip through the loop. This time, feof(infile) is true -- because
the last fgetc() failed due to EOF -- and we believe that the
file ended with a '\377' byte.

It also fails mysteriously (on those machines) when run with input
redirected to a file that is on a floppy with a bad sector. This
time, it loops forever. The reason is that, as before, fgetc()
returns EOF, which is stored in the plain (and thus unsigned)
"char", and then we compare 255 vs -1, so we continue to loop.
This time, however, feof(infile) is false, because the failure was
due to an I/O error rather than EOF.

There are a number of "morals of the story", as it were. The main
two are:

Never use feof(). (For the advanced C programmer: "Well, hardly
ever.")

Do not depend on undocumented "features" like alloca() returning
contiguous bytes, because these are not always true.

Of course, there is a way to do this without using alloca() at all,
so that the code will work on any system supporting C89 or C99; but
if the goal is "to use alloca()", using only the standard C89/C99
features will not suffice, since alloca() is not one of them. :)
 
B

bahadir.balban

CBFalconer said:
I'm leaping in here for two reasons. One is that you are a google
user who is using the system correctly, with adequate quotes etc.
So it IS possible. Congratulations, and it shows that we should
not automatically killfile all google users.
Thanks.

The other is the generic problem of off-topicness. This whole
thread is off-topic, yet where is the newbie to go?

Sorry about this. To be honest, I enjoyed the stack discussion, but I
must admit I didn't think enough to realise stacks were off-topic
before posting. I know how ugly it is when you see irrelevant/newbie
questions - I won't do it again.

Thanks,
Bahadir
 
O

Old Wolf

pete said:
If char is an unsigned type, then there's no way that
*ptr can equal EOF.

And if char is signed, then *ptr may equal EOF even though
the end of file has not been reached yet.

Also I don't see how the OP code could possibly work,
unless there's something about alloca nobody told me.
The next line of code was: memcpy(buf, ptr, i).

But ptr points to exactly one char, so if i>1 then he
is reading uninitialized memory. Also he has lost all
the chars he alloca'd in previous iterations of the loop.
 
J

Jean-Claude Arbaut

Le 16/06/2005 00:58, dans
(e-mail address removed), « Old Wolf »
Also I don't see how the OP code could possibly work,
unless there's something about alloca nobody told me.

Apart from the numerous bugs, it could actually work,
but it is *highly* system- and compiler- dependant.
(and no need to say, highly non-standard).

char *tmp;
char *ptr = alloca(sizeof(char));
int t;

for(;;) {
char *tmp = alloca(sizeof(char));
t = fgetc(infile);
*tmp = t;
if(*tmp == EOF) {
...
}
i++;
}

This will work if alloca allocates contiguous bytes
on the stack, and the final memcpy will work
dependind on which direction the stack grows.
If you try that, check asm output, because it's
very likely that the produced executable won't
do what you expect. And don't use that in
production code in any circumstances :)
The next line of code was: memcpy(buf, ptr, i).
But ptr points to exactly one char, so if i>1 then he
is reading uninitialized memory.
Also he has lost all
the chars he alloca'd in previous iterations of the loop.

We have lost nothing: previous chars were allocated
on the stack and initialized. We assume we know were
they actually are (just after the first char allocated).
 
M

Michael Wojcik

Sorry about this. To be honest, I enjoyed the stack discussion, but I
must admit I didn't think enough to realise stacks were off-topic
before posting.

IMO, the question as phrased was marginally on-topic, or perhaps just
the wrong side of topicality. There is to my mind a considerable
distance between "how are variables defined at inner scope allocated"
and, say, "how do I find out what's on my stack". The former is
something which conceivably might be (though in fact it is not)
covered by the standard, perhaps in some obscure fashion, while the
latter is clearly (to anyone who's taken a look at the standard
language features) implementation-specific.

Personally, I don't mind seeing questions about how language elements
are typically implemented, if they're posed well.

Another place for this sort of discussion is alt.folklore.computers,
where computing history is discussed. Unfortunately it's a relatively
high-volume group with a pronounced tendency to drift off-topic.
I know how ugly it is when you see irrelevant/newbie
questions - I won't do it again.

If all newbies were this careful with their posting style, I expect
there would be far less grumbling from the regulars.

--
Michael Wojcik (e-mail address removed)

Thus, the black lie, issuing from his base throat, becomes a boomerang to
his hand, and he is hoist by his own petard, and finds himself a marked man.
-- attributed to a "small-town newspaper editor in Wisconsin"
 
A

Anonymous 7843

int func(int a)
{
if (a > 0)
{
/* non-trivial return (usually) prevents tail-recursion elimination */
return other_func(a) * func(a-1);
}
else
{
struct gigantic_struct foo[1000000];
/* perform non-trivial processing of gigantic struct array */
/* return non-trivial value after processing */
}
}

Unless the compiler was particularly clever, it might grow
the stack at an alarming rate. I'll have to try this
one out with my favorite compilers and see what happens.

I tried this on three high quality compilers with full
optimization enabled. They each ate up the whole stack
within a few iterations.

I won't name names since this is probably not a valuable
thing to optimize in C. I was just curious.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top