x86 binary runs; x86_64 binary throws segfault

D

Don

argc can be 0 with *argv == NULL on some systems.  When that happens,
argv may be a pointer to (the start of) a single-element array in
which case *++argv does step outside the array.

Really? The program could be executed without its own name ever being
called?
Yes, you need to test *argv for NULL or, alternatively, for argc being
0.  This makes me wonder why you don't!

Well, I did, later on.
 What do you all think of this?
*******
while (*(++argv) != NULL && *argv[0] == '-') {

You have to test *argv not *++argv.  This alters almost everything
that follows, though you'll get away with it if there is always a
non-NULL argv[0].

I thought that there *was* always a non-NULL argv[0]. K&R sayeth, "By
convention, argv[0] is the name by which the program was invoked, so
argc is at least 1." Granted, that's not a statement of the standard,
but I'm having a hard time seeing how a program can be invoked without
it being invoked by some name, which would be argv[0].
*******
while (*argv != NULL && *argv[0] == '-') {
*******
Isn't that redundant anyway, though? If *argv[0] == '-', then we know
that *argv isn't null. But say I increment argv elsewhere; won't it
fail on the very first loop, unless it's called with a name beginning
with '-'?

What about the following:
*******
while (--argc > 0 && (*++argv)[0] == '-') {

}
   if (isdigit((*argv+1)[1]) || (*argv+1)[1] == '.')

I don't think you meant that.  argv[0][1] is the character that
follows the '-' you have just identified in the 'while' test.  If you
want to write that with fewer indexes, that would be *(argv[0] + 1) or
*(*argv + 1) or (*argv + 1)[0] but argv[0][1] is so much clearer than
any of the alternatives.  (*argv + 1)[1] is the second digit.  Did you
test with single digit negative number?

Yes; haven't figured that one out yet.
           break; /* negative numbers are not optional args */
   while (c = *++argv[0])

Whilst I don't object to modifying argv, modifying argv[0] worries me
a little.  It's permitted, but it complicates matters (debugging in
particular) so I prefer to set a 'flags' pointer to the current arg
and "walk" that.
           switch (c) {
           default:
                   fprintf(stderr,"dec:  illegal option \"%c\"\n",c);
                   break;
           }
}
if (*argv != NULL) {
   printf("%f\n",doztodec(*(argv)));
   return 0;
}
while (getword(doznum,MAXLINE) != EOF) {
   printf("%f\n",doztodec(doznum));
}
return 0;

<snip>
 
D

Don

*grumble* Stupid web interface I'm stuck with at work...
I thought that there *was* always a non-NULL argv[0].  K&R sayeth, "By
convention, argv[0] is the name by which the program was invoked, so
argc is at least 1."  Granted, that's not a statement of the standard,
but I'm having a hard time seeing how a program can be invoked without
it being invoked by some name, which would be argv[0].
*******
while (*argv != NULL && *argv[0] == '-') {
*******
Isn't that redundant anyway, though?  If *argv[0] == '-', then we know
that *argv isn't null.  But say I increment argv elsewhere; won't it
fail on the very first loop, unless it's called with a name beginning
with '-'?

What about the following:
*******
while (--argc > 0 && (*++argv)[0] == '-') {
while (c = *++argv[0])
/* option stuff here */
}
if (argc >= 1)
printf("%f\n",doztodec(*argv));
return 0;
}
while (getword(doznum,MAXLINE) != EOF) {
printf("%f\n",doztodec(doznum));
}
******
The first and second while tests I took out of K&R, p117. I'd tried
them before but they'd failed for reasons I couldn't understand; I
suppose it was probably due to other mistakes, because it looks to me
like it ought to work. Here, if I call it with "./dec "X44;4"", it'll
start by testing 1 > 0 and 'X' != '-', so it drops out of the loop.
It then sees that argc >= 1, so it executes that code and returns 0 to
the shell. If I call it with "./dec < testfile", it'll test 0 > 0,
drop out of the loop; it'll then see that argc < 1, so it'll go to the
second loop and input from stdin. If, on the other hand, I call it
with some future option, say "./dec -e "X44;4"", it'll start be
testing 2 > 0 and '-' == '-', so it'll execute the option code; it'll
then decrement argc and test 1 > 0, then increment argv and see that
'X' != '-', so it'll drop out and test argc, which is still 1, and run
the code there. But if I call it with "./dec -e < testfile", it'll
test 1 > 0 and '-' == '-', run the option code, decrement argc and
increment argv, then test 0 > 0, it'll fail, and it'll drop out of the
loop, see that argc < 1, and skip down to the second loop to receive
input from stdin.

It still doesn't cover the case where argv[0] is null, but otherwise,
does that sound about right?
   if (isdigit((*argv+1)[1]) || (*argv+1)[1] == '.')
I don't think you meant that.  argv[0][1] is the character that
follows the '-' you have just identified in the 'while' test.  If you
want to write that with fewer indexes, that would be *(argv[0] + 1) or
*(*argv + 1) or (*argv + 1)[0] but argv[0][1] is so much clearer than
any of the alternatives.  (*argv + 1)[1] is the second digit.  Did you
test with single digit negative number?

You're right. Plus, it doesn't account for the ';', 'X', and the
like. But to keep it simple for demonstrative purposes, what I mean
is rather this:
*******
if (isdigit(argv[0][1]) || argv[0][1] == ';')
*******
That should work, since argv now points to the current option. That
also explains the problems I couldn't figure out about single-digit
negative numbers. (Thanks; stupid of me, really.)

I busily rewriting expkill() to make sure I've got enough memory for
the new string, and to utilize strchr and memmove as you suggested.
I'll put it up when I've got something reasonably non-idiotic (I hope).
 
B

Ben Bacarisse

Don said:
Really? The program could be executed without its own name ever being
called?

Yes, really! The rather messily numbered 5.1.2.2.1 paragraph 2 only
states that:

2 If they are declared, the parameters to the main function shall obey
the following constraints:

- The value of argc shall be nonnegative.

- argv[argc] shall be a null pointer.

- If the value of argc is greater than zero, the array members
argv[0] through argv[argc-1] inclusive shall contain pointers to
strings, [...]

- If the value of argc is greater than zero, the string pointed to
by argv[0] represents the program name; [...]

[...]

argc can be 0 and then argv[0] will be a null pointer.
Well, I did, later on.

Too late by then -- you might have gone over the array.
 What do you all think of this?
*******
while (*(++argv) != NULL && *argv[0] == '-') {

You have to test *argv not *++argv.  This alters almost everything
that follows, though you'll get away with it if there is always a
non-NULL argv[0].

I thought that there *was* always a non-NULL argv[0]. K&R sayeth, "By
convention, argv[0] is the name by which the program was invoked, so
argc is at least 1." Granted, that's not a statement of the standard,
but I'm having a hard time seeing how a program can be invoked without
it being invoked by some name, which would be argv[0].

I doubt K&R2 says that. You can't rely on a statement about C as it
was more than 30 years ago!
*******
while (*argv != NULL && *argv[0] == '-') {
*******
Isn't that redundant anyway, though?

No, but if there is an argv[0] it tests the program name for starting
with a '-' which is probably not what you want.
If *argv[0] == '-', then we know
that *argv isn't null.

No you don't. You know that if *argv != NULL you can ask about
*argv[0] but if you don't test first that *argv != NULL you could get
any result at all from *argv[0] because it is undefined behaviour.
But say I increment argv elsewhere; won't it
fail on the very first loop, unless it's called with a name beginning
with '-'?

I don't follow this, sorry. The key point is that, to be portable,
you need to be aware that argc can be 0 and argv can point to a single
item that will (in that case) be a null pointer.
What about the following:
*******
while (--argc > 0 && (*++argv)[0] == '-') {

}

That's OK. Maybe not the clearest, but it's fine.

<snip>
 
B

Ben Bacarisse

Don said:
*grumble* Stupid web interface I'm stuck with at work...
I thought that there *was* always a non-NULL argv[0].  K&R sayeth, "By
convention, argv[0] is the name by which the program was invoked, so
argc is at least 1."  Granted, that's not a statement of the standard,
but I'm having a hard time seeing how a program can be invoked without
it being invoked by some name, which would be argv[0].
*******
while (*argv != NULL && *argv[0] == '-') {
*******
Isn't that redundant anyway, though?  If *argv[0] == '-', then we know
that *argv isn't null.  But say I increment argv elsewhere; won't it
fail on the very first loop, unless it's called with a name beginning
with '-'?

What about the following:
*******
while (--argc > 0 && (*++argv)[0] == '-') {
while (c = *++argv[0])
/* option stuff here */
}
Yup.

if (argc >= 1)
printf("%f\n",doztodec(*argv));

Missing { but, yes, that's fine. I don't find it that clear. I had
to think long and hard to see that the -- in the while has already
changed argc so this test is the correct one.

My personal taste is to process all non-flag arguments so that last
part would be a while rather than an if.
return 0;
}
while (getword(doznum,MAXLINE) != EOF) {
printf("%f\n",doztodec(doznum));
}
******
The first and second while tests I took out of K&R, p117. I'd tried
them before but they'd failed for reasons I couldn't understand; I
suppose it was probably due to other mistakes, because it looks to me
like it ought to work. Here, if I call it with "./dec "X44;4"", it'll
start by testing 1 > 0 and 'X' != '-', so it drops out of the loop.
It then sees that argc >= 1, so it executes that code and returns 0 to
the shell. If I call it with "./dec < testfile", it'll test 0 > 0,
drop out of the loop; it'll then see that argc < 1, so it'll go to the
second loop and input from stdin. If, on the other hand, I call it
with some future option, say "./dec -e "X44;4"", it'll start be
testing 2 > 0 and '-' == '-', so it'll execute the option code; it'll
then decrement argc and test 1 > 0, then increment argv and see that
'X' != '-', so it'll drop out and test argc, which is still 1, and run
the code there. But if I call it with "./dec -e < testfile", it'll
test 1 > 0 and '-' == '-', run the option code, decrement argc and
increment argv, then test 0 > 0, it'll fail, and it'll drop out of the
loop, see that argc < 1, and skip down to the second loop to receive
input from stdin.

It still doesn't cover the case where argv[0] is null, but otherwise,
does that sound about right?

Yes it does cover that case! If argv[0] is null argc *must* be zero
and neither while nor later if will do anything dangerous (at least I
don't think so).

<snip>
 
D

Don

argc can be 0 and then argv[0] will be a null pointer.

Right; it's within the standard. But I'm still having trouble seeing
how it's actually possible, in practice.
I thought that there *was* always a non-NULL argv[0].  K&R sayeth, "By
convention, argv[0] is the name by which the program was invoked, so
argc is at least 1."  Granted, that's not a statement of the standard,
but I'm having a hard time seeing how a program can be invoked without
it being invoked by some name, which would be argv[0].

I doubt K&R2 says that.  You can't rely on a statement about C as it
was more than 30 years ago!

Ha! For the first time on comp.lang.c, I'm right! :) Really,
that's a direct quotation from page 115 of the second edition. I'm an
attorney by trade, so every time I put quotation marks around
something that isn't an exact quote, a puppy dies; I'm therefore very
careful about it. And my edition was published in 1988, which is
considerably less computer-ancient than thirty years ago. Still
definitely ancient, though, I'll grant you.

I guess my question is this: granted that argv[0] == NULL is possible
under the standard, is it possible with any actual execution on a real
system? You've proven to me that it's *legal*, but I'm having a hard
time seeing how it could really happen.
That's OK.  Maybe not the clearest, but it's fine.

Thanks. As I mentioned, it didn't work originally, but there were so
many other things wrong with the initial program that it's worth
trying again.
 
B

Ben Bacarisse

Don said:
argc can be 0 and then argv[0] will be a null pointer.

Right; it's within the standard. But I'm still having trouble seeing
how it's actually possible, in practice.
I thought that there *was* always a non-NULL argv[0].  K&R sayeth, "By
convention, argv[0] is the name by which the program was invoked, so
argc is at least 1."  Granted, that's not a statement of the standard,
but I'm having a hard time seeing how a program can be invoked without
it being invoked by some name, which would be argv[0].

I doubt K&R2 says that.  You can't rely on a statement about C as it
was more than 30 years ago!

Ha! For the first time on comp.lang.c, I'm right! :) Really,
that's a direct quotation from page 115 of the second edition.

That's a shame. Errors are rare. I wonder if it is corrected in the
errata. I can't see to get the official list at the moment.
I'm an
attorney by trade, so every time I put quotation marks around
something that isn't an exact quote, a puppy dies; I'm therefore very
careful about it. And my edition was published in 1988, which is
considerably less computer-ancient than thirty years ago. Still
definitely ancient, though, I'll grant you.

I was not doubting your quote. You just did not say what edition it
was from and since errors in K&R2 are rare, some doubt it natural.
I guess my question is this: granted that argv[0] == NULL is possible
under the standard, is it possible with any actual execution on a real
system? You've proven to me that it's *legal*, but I'm having a hard
time seeing how it could really happen.

You can certainly make it happen by calling execv.

<snip>
 
D

Don

That's a shame.  Errors are rare.  I wonder if it is corrected in the
errata.  I can't see to get the official list at the moment.

It's not really an error, is it? After all, he says "y
convention," not "y standard." By convention that's certainly
true, isn't it?
You can certainly make it happen by calling execv.

Wow. execv is news to me. Okay, well, double thanks for helping me
account for that possible situation, then.

I'm working on rewriting expkill() using strchr() and memmove() and
having a hard time doing it. Basically, I think I can get it working
with any input except when that input would require padding the left
with zeroes. But I'll get there.
 
D

Don

I'm working on rewriting expkill() using strchr() and memmove() and
having a hard time doing it.  Basically, I think I can get it working
with any input except when that input would require padding the left
with zeroes.  But I'll get there.

While I'm at it, here's what I've worked out for ensuring that my
string has sufficient memory to hold the expanded number (if the
number has to expand, that is). As I see it, the problem is that argv
gives me a pointer to a string bounded by exactly enough memory to
hold the characters I see, plus '\0'. So, for example, if I run "./
dec "X44;4e5"", I've got 8 bytes of memory reserved. The expanded
string, though, should be "X4440000", requiring 9 bytes of memory
(including the '\0'). So I need to ensure that this allocated memory
for my string gets expanded by the appropriate amount.

Because I've been advised repeatedly not to be so stingy with
allocating memory, I'm going to go ahead and give myself more than I
need (probably), while ensuring that it's *at least* what I need
(definitely). So I'll allocate the length of the string, plus a
number of bytes equal to the exponent given. Prior to this, I've
already truncated the 'e' and the exponent. So, for "X44;4e5", then,
I'll wind up with 11 bytes reserved. As I understand it, sizeof()
returns the number of bytes allocated as an integer. So sizeof(s),
where s="X44;4e5", will return 8; however, where I've received the
string from stdin, I've allocated much more than a double can possibly
hold anyway, so I want to ensure that I'm not needlessly adding memory
when that's the case. So I want to make sure that I'm only increasing
the available memory if there's a chance the memory might be
insufficient. Here's what I've got:
*******
if (sizeof(s) < sizeof(strlen(s) + exp))
if (realloc(s, sizeof(strlen(s) + exp)) == NULL) {
fprintf(stderr,"dec: insufficient memory; converting %s\n",s);
return 0;
}
*******
I believe that this code will execute only if s has less memory
allocated than is sufficient to hold the current strlen of s, plus a
number of bytes equal to the value of my exponent, exp. If that's the
case, it reallocates memory to s such that s now has enough memory to
hold itself, plus exp more bytes. The characters allocated beyond the
'\0' that terminates s are garbage; however, those from 0 to strlen(s)
remain unaltered. If there isn't enough memory (which seems
exceedingly unlikely, but why not?), I print an error message and
return. This does return a meaningful value to the calling function
because the 'e' and the exponent have already been truncated when this
is called. They won't get the answer they're expecting, though, hence
the error message.

How's it look? Be gentle; it's my very first malloc/realloc/calloc/
etc call.
 
E

Eric Sosman

That's a shame. Errors are rare. I wonder if it is corrected in the
errata. I can't see to get the official list at the moment.

It's not really an error, is it? After all, he says "y
convention," not "y standard." By convention that's certainly
true, isn't it?
You can certainly make it happen by calling execv.

Wow. execv is news to me. Okay, well, double thanks for helping me
account for that possible situation, then.


Ben has thrown you a curve: The execve() function is not
part of C, but part of the POSIX standards for Unix-style
systems. In those systems, it's one of a family of functions
whose job is to start a new program, essentially by terminating
the current program and launching a new one to replace it.

By using exec*() on POSIX systems, you can start a new
program with whatever command-line arguments you like, including
"none at all." You can also provide an argv[0] that has nothing
to do with the "name" of the program -- indeed, some POSIX
shells do this on purpose.
I'm working on rewriting expkill() using strchr() and memmove() and
having a hard time doing it. Basically, I think I can get it working
with any input except when that input would require padding the left
with zeroes. But I'll get there.

"Back at the ranch," as they say, here's a thought you might
want to ponder: What is the purpose of padding with leading or
trailing zeroes? That is, what will your program do differently
with "123;" than with "123000;" or ";000123"? And here's the
crucial question: Do you actually need those zeroes to get the
effect you want, or could you get it some other way? If nothing
comes to mind, study the way your fracval() function computes
and uses the `divisor' value ...
 
K

Keith Thompson

Don said:
I guess my question is this: granted that argv[0] == NULL is possible
under the standard, is it possible with any actual execution on a real
system? You've proven to me that it's *legal*, but I'm having a hard
time seeing how it could really happen.
[...]

On Unix-like systems, programs are invoked via the "exec" family of
functions (execl, execlp, execle, execv, execvp). Some or all of
these let the caller specify the argv values that the invoked program
will see. (When you run a program by typing a command to the shell,
the shell parses your command line and calls one of the exec*
functions.)
 
D

Don

     "Back at the ranch," as they say, here's a thought you might
want to ponder: What is the purpose of padding with leading or
trailing zeroes?  That is, what will your program do differently
with "123;" than with "123000;" or ";000123"?  And here's the
crucial question: Do you actually need those zeroes to get the
effect you want, or could you get it some other way?  If nothing
comes to mind, study the way your fracval() function computes
and uses the `divisor' value ...

Ah. Clever. Just truncate the exponent off after saving its value
and pass it to fracdec(), then loop up to it, multiplying divisor by
12 each time. Do the same with exponents the other way, passing them
to wholdec() and working with them there. Let's see; to determine how
many extra passes through the loop are required:
*******
passes = semi + exp;
if (passes > strlen(s))
passes -= strlen(s);
*******
So if my number is "X;444e-4", I get passes == -3. If it's "X;444e6",
after exponent truncation, I get passes == 3. I then put the
semicolon first in the former case and strip it out in the second.
Then I test if the semicolon is first; if so, I give passes to
fracdec; if not, I give it to wholedec. In both, I run the operations
the appropriate number of extra times, then return the value as
before. But I only need to do this if it pushes the semicolon either
left or right beyond string boundaries; otherwise, I just move the
semicolon, which is pretty easy with strchr and memmove.

Is that what you're driving at?
 
B

Ben Bacarisse

Don said:
That's a shame.  Errors are rare.  I wonder if it is corrected in the
errata.  I can't see to get the official list at the moment.

It's not really an error, is it? After all, he says "y
convention," not "y standard." By convention that's certainly
true, isn't it?


I don't have the book, so I can't be sure of the context. Maybe it's
fine, but you certainly seem to have taken it the wrong way. Is it
clear form the context that argc can be 0 with argv[0] == 0 as the
only element in argv? If so, it's fine.

<snip>
 
D

Don

*******
passes = semi + exp;
if (passes > strlen(s))
     passes -= strlen(s);
*******

Well, that's wrong. But I've got something I think is right. I'll
get it home tonight and compile it, then run it through my eternally
increasing battery of tests, and once I've got it working I'll post it
here. Then you all can proceed to tell me all the ways that it'll
overrun its buffers, increase global warming, and murder innocent
kittens so I can get to fixing it.

This thread has improved my code and my approach to programming in
general more than the last month of working on my own. Thank you all
immensely.
 
K

Keith Thompson

Don said:
Well, that's wrong.
[...]

Without looking at the context, I can't comment on whether it's wrong,
but it is inefficient, since you call strlen twice on the same string.
Save the result in a variable (a size_t, not an int):

const size_t len = strlen(s);
if (passes > len) {
passes -= len;
}

strlen() can be a bit deceptive; it looks simple, but it can be
expensive. An even worse example is:

for (i = 0; i < strlen(s); i ++) {
/* ... */
}

which turns what should be an O(N) loop into an O(N**2) loop.
 
E

Eric Sosman

[...] So I want to make sure that I'm only increasing
the available memory if there's a chance the memory might be
insufficient. Here's what I've got:
*******
if (sizeof(s)< sizeof(strlen(s) + exp))
if (realloc(s, sizeof(strlen(s) + exp)) == NULL) {
fprintf(stderr,"dec: insufficient memory; converting %s\n",s);
return 0;
}
*******

This cannot work, except by amazing coincidence. Here's why:

- sizeof(s) reports the number of bytes occupied by s. So,
what is s? If it's an array, you get the number of array
elements times the sizeof each element (1, in this case).
But if s is a pointer, a char*, you get the number of bytes
in the pointer, not the number of bytes in the memory it
points to.

- If s is an array, you did not obtain it from malloc() and
company. Therefore, you cannot resize it with realloc().
(The compiler might allow you to try, in much the same way
that a divided highway might allow you to try driving the
wrong way. In both cases, a crash is likely.)

There is also evidence here of a fundamental misunderstanding
of the sizeof operator and of the way realloc() works. To put it
more bluntly, you don't know what you're doing, and you need to
revisit your textbook. Making semi-random semi-plausible changes
until something sort of seems to kind of work in a few cases is
not the way to program.
How's it look? Be gentle; it's my very first malloc/realloc/calloc/
etc call.

Okay, I'll be gentle: I'll let you put bubble wrap on your
head before I apply the clue-by-four. Ready? ...

(Back to the books, laddie. Usenet is a fine vehicle for
some things, but it's not a good channel for inculcating wholesale
lots of basic information.)
 
D

Don

Ouch. You could have at least let me put the bubble-wrap on *before*
applying the cluebat.
     (Back to the books, laddie.  Usenet is a fine vehicle for
some things, but it's not a good channel for inculcating wholesale
lots of basic information.)

Okay; I thought I got the memory allocation functions. Obviously I
don't. I'll be back. (Of course, it doesn't look like I'll need to
worry about it anyway, if I can get your other idea working.)

The only textbook I have to go with is K&R, which says precious little
about sizeof, malloc, and friends. Thus, I tried to go from what it
says in its description of the standard libraries, which clearly
wasn't enough for me. Any recommendations of where to go for more?
 
B

Ben Bacarisse

Don said:
While I'm at it, here's what I've worked out for ensuring that my
string has sufficient memory to hold the expanded number (if the
number has to expand, that is). As I see it, the problem is that argv
gives me a pointer to a string bounded by exactly enough memory to
hold the characters I see, plus '\0'. So, for example, if I run "./
dec "X44;4e5"", I've got 8 bytes of memory reserved. The expanded
string, though, should be "X4440000", requiring 9 bytes of memory
(including the '\0'). So I need to ensure that this allocated memory
for my string gets expanded by the appropriate amount.

You can't expand a memory region you've not allocated. You can't
increase the space provided for command-line arguments. You'll need
to make some new storage a copy the final value there (but see later).
Because I've been advised repeatedly not to be so stingy with
allocating memory, I'm going to go ahead and give myself more than I
need (probably), while ensuring that it's *at least* what I need
(definitely). So I'll allocate the length of the string, plus a
number of bytes equal to the exponent given.

That's a reasonable strategy.
Prior to this, I've
already truncated the 'e' and the exponent. So, for "X44;4e5", then,
I'll wind up with 11 bytes reserved. As I understand it, sizeof()
returns the number of bytes allocated as an integer.

No. sizeof returns the number of bytes used to represent it's operand
(it's not a function)...
So sizeof(s),
where s="X44;4e5", will return 8;

If s is char *, it will tell you the size of the character pointers on
your machine. Use strlen(s) + 1 to find out how many bytes there are
in the string s points to.
however, where I've received the
string from stdin, I've allocated much more than a double can possibly
hold anyway, so I want to ensure that I'm not needlessly adding memory
when that's the case. So I want to make sure that I'm only increasing
the available memory if there's a chance the memory might be
insufficient. Here's what I've got:
*******
if (sizeof(s) < sizeof(strlen(s) + exp))
if (realloc(s, sizeof(strlen(s) + exp)) == NULL) {
fprintf(stderr,"dec: insufficient memory; converting %s\n",s);
return 0;
}
*******

You can't realloc here because you did not malloc s (at least you
don't show us that code if you do). The sizeof stuff is wrong and the
realloc call is wrong also. Rather than type an essay about this, I'd
rather suggest you read K&R some more. You need to look at sizeof,
strlen and realloc.
I believe that this code will execute only if s has less memory
allocated than is sufficient to hold the current strlen of s, plus a
number of bytes equal to the value of my exponent, exp. If that's the
case, it reallocates memory to s such that s now has enough memory to
hold itself, plus exp more bytes. The characters allocated beyond the
'\0' that terminates s are garbage; however, those from 0 to strlen(s)
remain unaltered. If there isn't enough memory (which seems
exceedingly unlikely, but why not?), I print an error message and
return. This does return a meaningful value to the calling function
because the 'e' and the exponent have already been truncated when this
is called. They won't get the answer they're expecting, though, hence
the error message.

How's it look? Be gentle; it's my very first malloc/realloc/calloc/
etc call.

I don't know why you are allocating anything. Having found the
location of the ; and the value of the exponent I think you can just
print the result without storing it anywhere. I may not have all the
details of what you are trying to do yet, but I'd think about it.
 
E

Eric Sosman

Ouch. You could have at least let me put the bubble-wrap on *before*
applying the cluebat.


Okay; I thought I got the memory allocation functions. Obviously I
don't. I'll be back. (Of course, it doesn't look like I'll need to
worry about it anyway, if I can get your other idea working.)

The only textbook I have to go with is K&R, which says precious little
about sizeof, malloc, and friends. Thus, I tried to go from what it
says in its description of the standard libraries, which clearly
wasn't enough for me. Any recommendations of where to go for more?

My K&R is the original, now valuable more for sentimental
than for practical reasons. I've not seen the second edition,
and can't comment on it.

A good source for the avid learner is the comp.lang.c
Frequently Asked Questions (FAQ) site, <http://www.c-faq.com/>.
Even if some of its topics seem irrelevant to you now, I'd
recommend that you at least skim them so that someday in mid-
quandary you'll think "Wasn't this in the FAQ somewhere?"
Also, it includes a section on resources (showing some age,
but it's a starting point).
 
D

Don

     A good source for the avid learner is the comp.lang.c
Frequently Asked Questions (FAQ) site, <http://www.c-faq.com/>.
Even if some of its topics seem irrelevant to you now, I'd
recommend that you at least skim them so that someday in mid-
quandary you'll think "Wasn't this in the FAQ somewhere?"
Also, it includes a section on resources (showing some age,
but it's a starting point).

Thanks for the link, both of you; I've been poring over it all
weekend. My central problem seems to have been overgeneralization. I
have, indeed, overgeneralized a lot of what I've read in K&R,
particularly the relationship between arrays and pointers. For
example, because I declared an array of char, then passed a pointer to
that array to a function, I tried to apply sizeof to it in that
function as if it were an array, which of course it isn't. And, as a
corollary, I completely misunderstood sizeof and the alloc crew, as
you mentioned. This link has been a big help. I've written a small
program which uses malloc() and free(), apparently successfully (at
least, valgrind accepts it as leakless), and I've written in bounds-
checking so that it works rationally no matter how ridiculous the
input.

The FAQ also references some places where I can read some code, which
has already cleared up a lot for me and will continue to do so. I
think I need to spend a good deal of time reading code, as well as
writing it, to make the idioms of C more second-nature to me, to avoid
at least the stupider gotchas. I've already caught myself writing to
memory prior to the start of an array, although it only happened with
some particularly absurd input, and fixed it. The wisdom of writing
in code to deal with edge cases and bad input at the beginning, as
someone here already pointed out to me, is becoming very clear.

After I've spent some time coming to grok this information, I'll
rewrite this converter and marvel at how awful my previous versions
were. Thanks again.
 

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
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top