By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,168 Members | 1,194 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,168 IT Pros & Developers. It's quick & easy.

indentify if argument is char or float

P: n/a
hi all,
i building an equition with float type now,what function should i use
in order to identify is someone enter a char value by mistake?

Aug 5 '06 #1
Share this Question
Share on Google+
34 Replies


P: n/a
ya******@gmail.com said:
hi all,
i building an equition with float type now,what function should i use
in order to identify is someone enter a char value by mistake?
Capture the input as a string.
Use strtod to convert to floating-point form.
Use the ability strtod gives you to identify the position of the first
character that could not be used in the conversion.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 5 '06 #2

P: n/a
hi richard,
do you know maybe on a function C that i can use,its just need o check
if its a float value or non float number

Richard Heathfield כתב:
ya******@gmail.com said:
hi all,
i building an equition with float type now,what function should i use
in order to identify is someone enter a char value by mistake?

Capture the input as a string.
Use strtod to convert to floating-point form.
Use the ability strtod gives you to identify the position of the first
character that could not be used in the conversion.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 5 '06 #3

P: n/a
ya******@gmail.com said:
Richard Heathfield said:
>ya******@gmail.com said:
hi all,
i building an equition with float type now,what function should i use
in order to identify is someone enter a char value by mistake?

Capture the input as a string.
Use strtod to convert to floating-point form.
Use the ability strtod gives you to identify the position of the first
character that could not be used in the conversion.

do you know maybe on a function C that i can use,its just need o check
if its a float value or non float number
Oh, I see. Well, in that case I suggest you capture the data as a string,
use strtod to convert to floating-point form, and use the ability strtod
gives you to identify the position of the first character that could not be
used in the conversion.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 5 '06 #4

P: n/a
On 2006-08-05, Richard Heathfield <in*****@invalid.invalidwrote:
ya******@gmail.com said:
>Richard Heathfield said:
>>ya******@gmail.com said:

hi all,
i building an equition with float type now,what function should i use
in order to identify is someone enter a char value by mistake?

Capture the input as a string.
Use strtod to convert to floating-point form.
Use the ability strtod gives you to identify the position of the first
character that could not be used in the conversion.

do you know maybe on a function C that i can use,its just need o check
if its a float value or non float number

Oh, I see. Well, in that case I suggest you capture the data as a string,
use strtod to convert to floating-point form, and use the ability strtod
gives you to identify the position of the first character that could not be
used in the conversion.
Another way would be to capture the data as a string, use strtol to convert
to integer-form, and use the ability strtol gives you to identify the
position of the first character that could not be used in the conversion.

--
Andrew Poelstra <http://www.wpsoftware.net/projects>
To reach me by email, use `apoelstra' at the above domain.
"Do BOTH ends of the cable need to be plugged in?" -Anon.
Aug 5 '06 #5

P: n/a
Andrew Poelstra said:

<snip>
>
Another way would be to capture the data as a string, use strtol to
convert to integer-form, and use the ability strtol gives you to identify
the position of the first character that could not be used in the
conversion.
That wouldn't help him a lot with floating-point, which he specifically
mentioned he was using.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 5 '06 #6

P: n/a
On 2006-08-05, Richard Heathfield <in*****@invalid.invalidwrote:
Andrew Poelstra said:

<snip>
>>
Another way would be to capture the data as a string, use strtol to
convert to integer-form, and use the ability strtol gives you to identify
the position of the first character that could not be used in the
conversion.

That wouldn't help him a lot with floating-point, which he specifically
mentioned he was using.
Oh; by `char' did he mean character? I was thinking tiny integer, and
figured that if strtol() said that a '.' was the first invalid character
(and the character after that was a digit), he could consider it floating
point.

--
Andrew Poelstra <http://www.wpsoftware.net/projects>
To reach me by email, use `apoelstra' at the above domain.
"Do BOTH ends of the cable need to be plugged in?" -Anon.
Aug 5 '06 #7

P: n/a
Andrew Poelstra said:
On 2006-08-05, Richard Heathfield <in*****@invalid.invalidwrote:
>Andrew Poelstra said:

<snip>
>>>
Another way would be to capture the data as a string, use strtol to
convert to integer-form, and use the ability strtol gives you to
identify the position of the first character that could not be used in
the conversion.

That wouldn't help him a lot with floating-point, which he specifically
mentioned he was using.

Oh; by `char' did he mean character?
I think what he meant was that he was expecting:

3.14159

and was worried that he might get

THREE POINT ONE FOUR ONE FIVE NINE

or

ELEPHANT

instead.
I was thinking tiny integer, and
figured that if strtol() said that a '.' was the first invalid character
(and the character after that was a digit), he could consider it floating
point.
When you are asking the user for, say, the width of a square, it is not
unreasonable for the user to be able to enter a value without a decimal
point, or with one, and still expect the program to be able to calculate
that square's area. So the presence or absence of '.' is not a good test.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 5 '06 #8

P: n/a

ya******@gmail.com wrote:
hi all,
i building an equition with float type now,what function should i use
in order to identify is someone enter a char value by mistake?
You should be able to write a simple fuzzer that hits your arguments
to make sure your program is properly checking inputs. Actually,
I would take this time to write a fuzzer to just learn how they are
built.
Ruby or Perl would both be good command line choices.

Here's an implementation of what the guys above are talking about:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv) {

char **endptr;
double output;

if(argc < 2) {
(void)printf("usage: strtof <digit>\n");
exit(1);
}

output = strtod(argv[1], endptr);
if (output == 0) {
perror("string to double conversion failed");
exit(1);
}
(void)printf("%f\n", output);
exit(0);
}

Aug 6 '06 #9

P: n/a
bw*****@yahoo.com said:

<snip>
>
Here's an implementation of what the guys above are talking about:

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char **argv) {

char **endptr;
Huh?

<snip>
output = strtod(argv[1], endptr);
Wrong.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #10

P: n/a


output = strtod(argv[1], endptr);

Wrong.
Why is this wrong? If all he is doing is testing to see whether the
input is valid, then all he needs to know is if strtod can convert. If
you fuzz the input with anything but numbers, the conversion will fail.

$ gdb strtof
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "amd64-unknown-openbsd4.0"...
(gdb) break main
Breakpoint 1 at 0x40089f: file strtof.c, line 12.
(gdb) run aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Starting program: /anime/strtof
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Breakpoint 1, main (argc=2, argv=0x7f7ffffd4870) at strtof.c:12
12 if(argc < 2) {
(gdb) step
17 output = strtod(argv[1], endptr);
(gdb) info locals
endptr = (char **) 0x7f7ffffd4860
output = 6.9261942529720109e-310
(gdb) until
18 if (output == 0) {
(gdb) info locals
endptr = (char **) 0x7f7ffffd4860
output = 0
(gdb) p *endptr
$1 = 0x7f7ffffd4d26 'a' <repeats 46 times>

Please explain why this is a bad approach.

Aug 6 '06 #11

P: n/a
bw*****@yahoo.com said:
output = strtod(argv[1], endptr);

Wrong.

Why is this wrong?
Because you're evaluating an indeterminate value - to wit, endptr.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #12

P: n/a
Richard Heathfield wrote:
bw*****@yahoo.com said:
output = strtod(argv[1], endptr);

Wrong.
Why is this wrong?

Because you're evaluating an indeterminate value - to wit, endptr.
It should be:
output = strtod(argv[1], &endptr), and endptr
should be declared as char *.

But I don't understand why you will consider
it a failure if the user enters "0"--that seems like a valid
input. It might be more appropriate to check
for *endptr == argv[1], or perhaps **endptr == '\0'.

Aug 6 '06 #13

P: n/a

Bill Pursell wrote:
It might be more appropriate to check
for *endptr == argv[1], or perhaps **endptr == '\0'.
Now, I do agree with that approach. That's a far better way
to check the value. You would also check for ERANGE
and limits within HUGE_VAL.

This approach is documented in strtol(3).

Brian

Aug 6 '06 #14

P: n/a
Bill Pursell said:

<snip>
It might be more appropriate to check
for *endptr == argv[1], or perhaps **endptr == '\0'.
No, because once you define endptr properly as char *, *endptr will be a
char, not a char *.

But you're right in that finding out where the conversion stopped happening
is your first step in validating the data.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #15

P: n/a
On Sat, 05 Aug 2006 17:10:51 +0000, Richard Heathfield wrote:
Andrew Poelstra said:

<snip>
>>
Another way would be to capture the data as a string, use strtol to
convert to integer-form, and use the ability strtol gives you to
identify the position of the first character that could not be used in
the conversion.

That wouldn't help him a lot with floating-point, which he specifically
mentioned he was using.

Using strtol is possible, but it must be used thoughtfully:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

/*
* Validates numbers of the form n{0,6}[.[n{0,6}]]
*/

float
validate_float(const char *data)
{
long whole, fractional;
static long powers [] = {1, 10, 100, 1000, 10000, 100000, 1000000};
#define MAXFRACT (sizeof(powers)/sizeof(powers[0])-1)
char *decptr, *endptr;
int decimals;
float number;

/* Nip out empty strings and naked decimal. NOTE: Adjust to locale. */
if (!strlen(data) || !strcmp(data, ".")) return -1;

/* Get whole part */
whole = strtol(data, &decptr, 10);

/* Verify that whole part is in range */
if (whole < 0 || whole == LONG_MAX) return -1;

/* Return whole number here. */
if (!*decptr) return whole;

/* Demand decimal point. NOTE: Adjust to locale. */
if (*decptr != '.') return -1;

/* Get fractional part if any */
fractional = strtol(decptr+1, &endptr, 10);

/* Check fractional part for bad characters */
if (*endptr) return -1;

/* Verify that fractional part is in range. */
if (fractional < 0 || endptr - (decptr+1) MAXFRACT) return -1;

/* Make fractional adjustment */
decimals = strlen(decptr+1);
if (decimals MAXFRACT) decimals = MAXFRACT;

/* Calculate number */
number = whole + (double)fractional/powers[decimals];
return number;
}

int
main(int argc, char *argv[])
{
double converted;
#define DOSTRING(STRING) do { \
converted = validate_float(STRING); \
if (converted < 0) printf("invalid '"STRING"'\n"); \
else printf(STRING" -%f\n", converted); \
} while(0)
/* Invalid */
DOSTRING("");
DOSTRING(".");
DOSTRING("0.x");
DOSTRING("0.0x");
DOSTRING("123456.1234567");
DOSTRING("-1");

/* Valid */
DOSTRING("0");
DOSTRING(".0");
DOSTRING("0.");
DOSTRING("0.0");
DOSTRING("1");
DOSTRING(".1");
DOSTRING("1.");
DOSTRING("1.1");
DOSTRING("123456");
DOSTRING(".123456");
DOSTRING("1.123456");

return 0;
}
invalid ''
invalid '.'
invalid '0.x'
invalid '0.0x'
invalid '123456.1234567'
invalid '-1'
0 -0.000000
..0 -0.000000
0. -0.000000
0.0 -0.000000
1 -1.000000
..1 -0.100000
1. -1.000000
1.1 -1.100000
123456 -123456.000000
..123456 -0.123456
1.123456 -1.123456
Gratuitously,

Martin
--
Martin Golding | You don't need computers to screw things up,
DoD #236 BMWMOA #55952 | but it does make the work go faster. (Ed)
fo*****@comcast.net Vancouver, WA

Aug 6 '06 #16

P: n/a
Martin Golding said:
On Sat, 05 Aug 2006 17:10:51 +0000, Richard Heathfield wrote:
>Andrew Poelstra said:
<snip>
>>Another way would be to capture the data as a string, use strtol to
convert to integer-form, and use the ability strtol gives you to
identify the position of the first character that could not be used in
the conversion.

That wouldn't help him a lot with floating-point, which he specifically
mentioned he was using.

Using strtol is possible, but it must be used thoughtfully:
<snip>
invalid '123456.1234567'
invalid '-1'
On what grounds do you reject these? And why does your code reject 1e-6 ?

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #17

P: n/a

Bill Pursell wrote:
>
But I don't understand why you will consider
it a failure if the user enters "0"--that seems like a valid
input. It might be more appropriate to check
for *endptr == argv[1], or perhaps **endptr == '\0'.
I changed endptr to a char *. I don't think it really matters as I
would be de-referencing that variable anyway. I would like to hear why
you prefer that approach. Is it code readability?

Here's a working version:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>

int
main(int argc, char **argv) {

char *endptr;
char *input;
double output;

input = argv[1];
if(argc < 2) {
(void)printf("usage: strtof <digit>\n");
exit(1);
}

output = strtod(input, &endptr);
if (input[0] == '\0' || *endptr != '\0') {
perror("string to double conversion failed");
exit(1);
}
(void)printf("%f\n", (float)output);
exit(0);
}

Here are some debugging results:

$ gdb strtof
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "amd64-unknown-openbsd4.0"...
(gdb) break main
Breakpoint 1 at 0x40089f: file strtof.c, line 13.
(gdb) run 0a1b2c
Starting program: /anime/strtof 0a1b2c

Breakpoint 1, main (argc=2, argv=0x7f7ffffec080) at strtof.c:13
13 input = argv[1];
(gdb) info locals
endptr = 0x7f7ffffec070 ""
input = 0x7f7ffffec528 "/anime/strtof"
output = 4.1461860541929491e-317
(gdb) step
14 if(argc < 2) {
(gdb) step
19 output = strtod(input, &endptr);
(gdb) until
20 if (input[0] == '\0' || *endptr != '\0') {
(gdb) info locals
endptr = 0x7f7ffffec537 "a1b2c"
input = 0x7f7ffffec536 "0a1b2c"
output = 0
(gdb) p *endptr
$1 = 97 'a'
(gdb) p input[0]
$2 = 48 '0'
(gdb) step
21 perror("string to double conversion failed");
(gdb)

and here are some results to show that I can now enter a 0 as a valid
entry:

$ gdb strtof
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "amd64-unknown-openbsd4.0"...
(gdb) break main
Breakpoint 1 at 0x40089f: file strtof.c, line 13.
(gdb) run 0
Starting program: /anime/strtof 0

Breakpoint 1, main (argc=2, argv=0x7f7fffff5d18) at strtof.c:13
13 input = argv[1];
(gdb) info locals
endptr = 0x7f7fffff5d00 ""
input = 0x7f7fffff61c0 "/anime/strtof"
output = 4.1461860541929491e-317
(gdb) step
14 if(argc < 2) {
(gdb) step
19 output = strtod(input, &endptr);
(gdb) until
20 if (input[0] == '\0' || *endptr != '\0') {
(gdb) info locals
endptr = 0x7f7fffff61cf ""
input = 0x7f7fffff61ce "0"
output = 0
(gdb) p input[0]
$1 = 48 '0'
(gdb) p *endptr
$2 = 0 '\0'
(gdb) step
24 (void)printf("%f\n", (float)output);
(gdb) info locals
endptr = 0x7f7fffff61cf ""
input = 0x7f7fffff61ce "0"
output = 0
(gdb)

Aug 6 '06 #18

P: n/a
bw*****@yahoo.com said:
>
Bill Pursell wrote:
>>
But I don't understand why you will consider
it a failure if the user enters "0"--that seems like a valid
input. It might be more appropriate to check
for *endptr == argv[1], or perhaps **endptr == '\0'.

I changed endptr to a char *. I don't think it really matters as I
would be de-referencing that variable anyway.
It matters very much, because strtod expects the /address/ of an object that
has type char *. You do it like this:

char *endptr;
double d = strtod(s, &endptr);

if(endptr == s)
{
no characters were converted, so you know that the input was not a valid
text representation of a double.
}

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #19

P: n/a

Richard Heathfield wrote:
It matters very much, because strtod expects the /address/ of an object that
has type char *. You do it like this:

char *endptr;
double d = strtod(s, &endptr);

I follow you. However, strtod expects a parameter of type char **. I
can pass the address of a pointer, or I can just pass a variable of
type char **. Either way, the only thing that changes is how I
dereference the pointer when the function returns.

Here's the same program with endptr declared as char **:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>

int
main(int argc, char **argv) {

char **endptr;
char *input;
double output;

input = argv[1];

if(argc < 2) {
(void)printf("usage: strtof <digit>\n");
exit(1);
}
output = strtod(input, endptr);
if (**endptr != '\0') { /* dereference of pointer changes */
perror("string to double conversion failed");
exit(1);
}
(void)printf("%f\n", (float)output);
exit(0);
}

The above passes gcc -Wall -pedantic without warning.
if(endptr == s)
{
no characters were converted, so you know that the input was not a valid
text representation of a double.
}
Now, if I entered:

12......222.....222

strtod() would convert the first two digits in your example and dump
them to output. If you really wanted to be particular, you could also
check for NOP sleds as well. Of course, this would reduce the
portability of your code.

Aug 6 '06 #20

P: n/a
bw*****@yahoo.com said:
>
Richard Heathfield wrote:
>It matters very much, because strtod expects the /address/ of an object
that has type char *. You do it like this:

char *endptr;
double d = strtod(s, &endptr);


I follow you. However, strtod expects a parameter of type char **. I
can pass the address of a pointer, or I can just pass a variable of
type char **.
Yes, that will /compile/, but it won't /work/.
Either way, the only thing that changes is how I
dereference the pointer when the function returns.
No. What changes is that, if you do this wrong, it won't work. Think about
*why* strtod needs a char **, and maybe enlightenment will occur, although
frankly I'm beginning to doubt that it will.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #21

P: n/a

Richard Heathfield wrote:
Yes, that will /compile/, but it won't /work/.
Here's a debugging session that shows that it doesn't matter what
approach you take:

with invalid input:

$ gdb strtof
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you ar
welcome to change it and/or distribute copies of it under certain
condition
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "amd64-unknown-openbsd4.0"...
(gdb) break main
Breakpoint 1 at 0x40089f: file strtof.c, line 13.
(gdb) run 1a1a1a1a1a
Starting program: /anime/strtof 1a1a1a1a1a

Breakpoint 1, main (argc=2, argv=0x7f7ffffc98a8) at strtof.c:13
13 input = argv[1];
(gdb) info locals
endptr = (char **) 0x7f7ffffc9890
input = 0x7f7ffffc9d50 "/anime/strtof"
output = 4.1461860541929491e-317
(gdb) step
15 if(argc < 2) {
(gdb) info locals
endptr = (char **) 0x7f7ffffc9890
input = 0x7f7ffffc9d5e "1a1a1a1a1a"
output = 4.1461860541929491e-317
(gdb) step
21 output = strtod(input, endptr);
(gdb) until
22 if (**endptr != '\0') {
(gdb) info locals
endptr = (char **) 0x7f7ffffc9890
input = 0x7f7ffffc9d5e "1a1a1a1a1a"
output = 1
(gdb) p **endptr
$1 = 97 'a'
(gdb) step
23 perror("string to double conversion failed");
(gdb)

with valid input:

$ gdb strtof
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and
you a
e
welcome to change it and/or distribute copies of it under certain
conditio
s.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details
This GDB was configured as "amd64-unknown-openbsd4.0"...
(gdb) break main
Breakpoint 1 at 0x40089f: file strtof.c, line 13.
(gdb) run 12.2
Starting program: /anime/strtof 12.2

Breakpoint 1, main (argc=2, argv=0x7f7ffffc8ca8) at strtof.c:13
13 input = argv[1];
(gdb) info locals
endptr = (char **) 0x7f7ffffc8c90
input = 0x7f7ffffc9150 "/anime/strtof"
output = 4.1461860541929491e-317
(gdb) step
15 if(argc < 2) {
(gdb) info locals
endptr = (char **) 0x7f7ffffc8c90
input = 0x7f7ffffc915e "12.2"
output = 4.1461860541929491e-317
(gdb) step
21 output = strtod(input, endptr);
(gdb) until
22 if (**endptr != '\0') {
(gdb) info locals
endptr = (char **) 0x7f7ffffc8c90
input = 0x7f7ffffc915e "12.2"
output = 12.199999999999999
(gdb) p **endptr
$1 = 0 '\0'
(gdb) step
26 (void)printf("%f\n", (float)output);
(gdb) until
12.200000
27 exit(0);
(gdb)

I have no problem being wrong. Please show me that the above does not
/work/.
I do agree that checking for output == 0 was wrong.

Aug 6 '06 #22

P: n/a
"bw*****@yahoo.com" <bw*****@yahoo.comwrites:
Richard Heathfield wrote:
>Yes, that will /compile/, but it won't /work/.

Here's a debugging session that shows that it doesn't matter what
approach you take:
[snip]

It shows no such thing. At best, it shows that it can *appear* to
work.

The second argument to strtod() is char **endptr. You pass a
pointer-to-pointer-to-char so that the function can *store* a
pointer-to-char value in the location you specify.

If you call it like this:

char **endptr; /* uninitialized */
... strtod("123", endptr) ...

you are passing the value of an uninitialized variable to the
function. An uninitialized pointer points to some arbitrary location
in memory; you're asking strtody() to write to that arbitrary
location. If it happens, by random chance, that that arbitrary
location is one that you're able to write to, the code will appear to
work -- but it's likely to clobber some other variable.

Don't do that.

By contrast, if you do this:

char *end;
... strtod("123", &end) ...

then "end" is uninitialized, but you're passing its *value*, not its
*address*, so that's ok. You're still passing a value of type char**
to strtod(), but now the value specifies the address of the char*
variable that you've declared. You're now asking strtod() to write a
value to "end" -- which is exactly what you want.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 6 '06 #23

P: n/a
bw*****@yahoo.com wrote:
Richard Heathfield wrote:
>Yes, that will /compile/, but it won't /work/.

Here's a debugging session that shows that it doesn't matter what
approach you take:
<snip>
I have no problem being wrong. Please show me that the above does not
/work/.
I do agree that checking for output == 0 was wrong.
The standard states that it invokes undefined behaviour. Therefore it
invokes undefined behaviour. One possible result of undefined behaviour
is it doing what you expect. Another possible result is your program
crashing. A third result is your computer being fried. A forth possible
result is your computer growing arms and legs, tying you up, putting you
in the boot of the car, and driving the car off a cliff killing you.

Richard Heathfield is a highly respected member of this group and
acknowledged around here as knowing a few things about C. You are just
learning. Think about who is more likely to be wrong and reread the
messages explaining why you are wrong.
--
Flash Gordon
Only about 11 years experience in C but over 20 years in programming.
A relative newbie for this group.
Aug 6 '06 #24

P: n/a
Keith Thompson said:

<snip>
>
By contrast, if you do this:

char *end;
... strtod("123", &end) ...

then "end" is uninitialized, but you're passing its *value*, not its
*address*, so that's ok.
Er, actually you're passing the value of its address. The expression is okay
because &end doesn't evaluate end. It yields the address of end. That
address's value is what strtod gets.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #25

P: n/a
bw*****@yahoo.com said:
>
Richard Heathfield wrote:
>Yes, that will /compile/, but it won't /work/.

Here's a debugging session that shows that it doesn't matter what
approach you take:
No, it's a debugging session that shows that you don't understand that
debugging sessions do not define the C language.

<snip>
I have no problem being wrong.
Evidently.
Please show me that the above does not /work/.
"If the subject sequence is empty or does not have the expected
form, no conversion is performed; the value of nptr is stored in the
object pointed to by endptr, provided that endptr is not a null
pointer."

If endptr does not point to an object (and is not NULL) then the behaviour
is necessarily undefined.
I do agree that checking for output == 0 was wrong.
This isn't a matter of debate. The C language definition says the way you're
doing it is wrong, and therefore the way you're doing it is wrong.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 6 '06 #26

P: n/a
Richard Heathfield <in*****@invalid.invalidwrites:
Keith Thompson said:
<snip>
>>
By contrast, if you do this:

char *end;
... strtod("123", &end) ...

then "end" is uninitialized, but you're passing its *value*, not its
*address*, so that's ok.

Er, actually you're passing the value of its address. The expression is okay
because &end doesn't evaluate end. It yields the address of end. That
address's value is what strtod gets.
Sorry, my fingers have betrayed me again.

What I meant, of course, was:

then "end" is uninitialized, but you're passing its *address*, not
its *value*, so that's ok.

As always, thanks for keeping me on my toes.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 6 '06 #27

P: n/a
Richard Heathfield <in*****@invalid.invalidwrites:
bw*****@yahoo.com said:
>>
Richard Heathfield wrote:
>>Yes, that will /compile/, but it won't /work/.

Here's a debugging session that shows that it doesn't matter what
approach you take:

No, it's a debugging session that shows that you don't understand that
debugging sessions do not define the C language.
Upthread, Richard advised you to *think* about why strtod() takes a
char** arguments. That was excellent advice; following it would have
saved you some time. Rather than thinking about it you experimented
and got some meaningless results.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 6 '06 #28

P: n/a
>Richard Heathfield wrote:
In article <11**********************@h48g2000cwc.googlegroups .com>

bw*****@yahoo.com <bw*****@yahoo.comwrote:
>I follow you. However, strtod expects a parameter of type char **. I
can pass the address of a pointer, or I can just pass a variable of
type char **.
Not exactly: you can pass the *value* of a variable of type
"char **". (You never pass "a variable" in C, only "a value".)
>Here's the same program with endptr declared as char **:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>

int
main(int argc, char **argv) {

char **endptr;
char *input;
double output;

input = argv[1];

if(argc < 2) {
(void)printf("usage: strtof <digit>\n");
exit(1);
}
output = strtod(input, endptr);
if (**endptr != '\0') { /* dereference of pointer changes */
perror("string to double conversion failed");
exit(1);
}
(void)printf("%f\n", (float)output);
exit(0);
}

The above passes gcc -Wall -pedantic without warning.
You should add "-O" (and -ansi, but in this case it is the -O that
is important):

% gcc -O -Wall -ansi -pedantic -o t t.c
t.c: In function `main':
t.c:9: warning: `endptr' might be used uninitialized in this function

The reason you need -O to get this warning is that gcc only runs
the dataflow analysis phase when optimizing, and it is the dataflow
analysis phase that discovers use of uninitialized variables.

If I move "char **endptr" down to the third variable, and compile
it on BSD/OS with shlicc (which uses gcc), without -O, and run the
resulting binary, I get:

% shlicc -o t t.c
% ./t 123.456
Segmentation fault (core dumped)

Turning on optimization makes it run, as does using "cc" (which
uses the dynamic linker instead of static shared libraries). Both
of these are "mere luck" (*bad* luck :-) ) -- with -O, gcc moves
the variables around, and when using the dynamic linker, the place
"endptr" lives on the stack happens to get some value stored in it
that points to some other valid address within the process. The
static shared library ("shlicc") version is much more efficient,
though, so that the stack is still "mostly clean" and endptr happens
to be NULL (as long as it is the third variable, not the first).
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Aug 6 '06 #29

P: n/a

Richard Heathfield wrote:
Er, actually you're passing the value of its address. The expression is okay
because &end doesn't evaluate end. It yields the address of end. That
address's value is what strtod gets.
I am going to spin this discussion off into another thread. I never
distinguished a difference between the two methods above of passing a
parameter.

Aug 6 '06 #30

P: n/a

Chris Torek wrote:
The reason you need -O to get this warning is that gcc only runs
the dataflow analysis phase when optimizing, and it is the dataflow
analysis phase that discovers use of uninitialized variables.
This helps a lot since I tend to use debugging sessions to find errors.
And I am wrong.

Thanks!

Aug 6 '06 #31

P: n/a
bw*****@yahoo.com wrote:
(gdb) info locals
endptr = (char **) 0x7f7ffffc9890
input = 0x7f7ffffc9d5e "1a1a1a1a1a"
output = 4.1461860541929491e-317
(gdb) step
21 output = strtod(input, endptr);
(gdb) until
strtod writes to the pointer you pass it. This debug trace shows
that your system allowed you to write to a random memory
address (ie. 7f7fffc9890) without any bad effects, in this case.

I'm sure many people will tell you that writing to a random
memory address can, in general, cause a segfault, or even
worse, the ability for violation of security privileges (and they
would be right).

Tip: If you ever apply for a C programming job, don't tell
the interviewer that you like to write code that writes to
random memory addresses.

Aug 7 '06 #32

P: n/a

Old Wolf wrote:
strtod writes to the pointer you pass it. This debug trace shows
that your system allowed you to write to a random memory
address (ie. 7f7fffc9890) without any bad effects, in this case.

I'm sure many people will tell you that writing to a random
memory address can, in general, cause a segfault, or even
worse, the ability for violation of security privileges (and they
would be right).

Tip: If you ever apply for a C programming job, don't tell
the interviewer that you like to write code that writes to
random memory addresses.
I think I follow you. It's because I never initialized char *endptr
since I declared it as char **endptr. So when I passed it as a
parameter,
the compiler randomly assigned the location of char *endptr.

And you don't have to worry about me applying for any C programming
job. I have worked in finance for the last 6 years.

I posted to this thread because I am writing a httpd server (off topic
and OS specific), and I need to practice parsing out the request
strings (not quite C strings). I figured this would give me a good
refresher on data validation. (I have never used strtod until
yesterday.) The good thing is that I learned something new by posting
code that wasn't quite right. I will keep posting code if it means I
will receive good responses.

Thanks!

Aug 7 '06 #33

P: n/a
Chris Torek said:
>>Richard Heathfield wrote:
In article <11**********************@h48g2000cwc.googlegroups .com>
Chris: No, I didn't.

<snip>

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Aug 7 '06 #34

P: n/a
av
On 6 Aug 2006 10:35:46 -0700, bw*****@yahoo.com wrote:
>Bill Pursell wrote:
>But I don't understand why you will consider
it a failure if the user enters "0"--that seems like a valid
input. It might be more appropriate to check
for *endptr == argv[1], or perhaps **endptr == '\0'.

I changed endptr to a char *. I don't think it really matters as I
would be de-referencing that variable anyway. I would like to hear why
you prefer that approach. Is it code readability?

Here's a working version:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>

int
main(int argc, char **argv) {

char *endptr;
why don't change above in:
char **endptr=0;
for me it has to segfault

the right definition should be
char *endptr=0;
Aug 8 '06 #35

This discussion thread is closed

Replies have been disabled for this discussion.