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

Using strtod

P: n/a
Hi experts,
Is the following usage of strtod okay (p is a char pointer):

value = strtod(p, &p);

Is it possible that this would evoke undefined behaviour?
Or should I use a temporary pointer and then assign its value to p?

Thanks

Feb 18 '07 #1
Share this Question
Share on Google+
18 Replies


P: n/a
coder wrote:
Hi experts,
Is the following usage of strtod okay (p is a char pointer):

value = strtod(p, &p);

Is it possible that this would evoke undefined behaviour?
Or should I use a temporary pointer and then assign its value to p?
You are allowed to do this as long as you bypass any possible macro
definition of strtod:

value = (strtod) (p, &p);

But it would be cleaner to just use a temporary.

Feb 18 '07 #2

P: n/a
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?
But it would be cleaner to just use a temporary.

Feb 18 '07 #3

P: n/a
coder wrote:
Hi experts,
Is the following usage of strtod okay (p is a char pointer):

value = strtod(p, &p);

Is it possible that this would evoke undefined behaviour?
Or should I use a temporary pointer and then assign its value to p?
You should use a temporary, not because of undefined
behavior (but see Harald van Dijk's response), but because
you will need the original pointer's value when you check
for errors. After strtod(p, &q), the condition p==q means
strtod() was unable to convert any of the input. If you
write strtod(p, &p) you will be unable to make the test.

--
Eric Sosman
es*****@acm-dot-org.invalid
Feb 18 '07 #4

P: n/a
coder wrote:
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>

Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);

I didn't get that. Can you please explain a little more?
strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.

Feb 18 '07 #5

P: n/a
coder wrote:
>
Is the following usage of strtod okay (p is a char pointer):

value = strtod(p, &p);

Is it possible that this would evoke undefined behaviour? Or
should I use a temporary pointer and then assign its value to p?
Why not read the description of the function? Note the
'restrict'. From N869:

7.20.1.3 The strtod, strtof, and strtold functions

Synopsis

[#1]
#include <stdlib.h>
double strtod(const char * restrict nptr,
char ** restrict endptr);
float strtof(const char * restrict nptr,
char ** restrict endptr);
long double strtold(const char * restrict nptr,
char ** restrict endptr);

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
<http://www.securityfocus.com/columnists/423>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews

Feb 18 '07 #6

P: n/a
CBFalconer wrote:
coder wrote:

Is the following usage of strtod okay (p is a char pointer):

value = strtod(p, &p);

Is it possible that this would evoke undefined behaviour? Or
should I use a temporary pointer and then assign its value to p?

Why not read the description of the function? Note the
'restrict'. From N869:

7.20.1.3 The strtod, strtof, and strtold functions

Synopsis

[#1]
#include <stdlib.h>
double strtod(const char * restrict nptr,
char ** restrict endptr);
float strtof(const char * restrict nptr,
char ** restrict endptr);
long double strtold(const char * restrict nptr,
char ** restrict endptr);
I'm pretty sure restrict is irrelevant here. It means the behaviour's
undefined if you do

char *p;
// ...
strtod((char *) &p, &p);

which could otherwise be defined, but it says nothing about

char *p;
// ...
strtod(p, &p);

Feb 18 '07 #7

P: n/a
On 18 Feb 2007 10:54:18 -0800, "Harald van D?k" <tr*****@gmail.com>
wrote in comp.lang.c:
coder wrote:
On Feb 18, 10:47 pm, "Harald van D?k" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?

strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.
Chapter and verse, please.

There is nothing at all in the standard that states that strtod() or
the other related strto... functions do not write to the pointer
addressed by their second argument until after they have finished all
use of the first argument.

You may say that were you to implement such a function, you would not
store the pointer value until you were finished with all other
processing. You may say that there is no need for any reasonable
implementation of strtod() to do so.

But unless you can point to wording in the standard that states this,
your answer is wrong and the OP's code produces undefined behavior.

There is absolutely nothing at all preventing an implementation from
providing a strtod() function like this:

double strtod(const char * restrict nptr, char ** restrict endptr)
{
double result = 0.0;
*endptr = NULL;

while (*nptr++ != '\0')
{
/* processing */
}
*endptr = nptr;
}

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Feb 19 '07 #8

P: n/a
In article <5s********************************@4ax.com>,
Jack Klein <ja*******@spamcop.netwrote:
>On 18 Feb 2007 10:54:18 -0800, "Harald van D?k" <tr*****@gmail.com>
wrote in comp.lang.c:
>strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write.
>Chapter and verse, please.
There is a sequence point after evaluation of the arguments
to a function, but before the function is called. Copies of the
value of the arguments are passed, not pass by reference.

Therefore, strtod(p,&p) would have to read p and calculate the
address of p, saving the read value and the address (reading
and saving done in any order); then the sequence point
requirement requires that the read and address calculation be
completed and the copies fully saved before the function is
invoked. The function would receive a value in the first
parameter that -happened- to contain the same contents as
could be found by dereferencing the pointer in the second
argument at that time, but there would be no way for the
function body to write into the pointer before that value-
copy had been taken because of the sequence point.

But as Harald pointed out, the sequence point requirements do
not apply to macros, so implementing as a macro could have different
results.
--
"No one has the right to destroy another person's belief by
demanding empirical evidence." -- Ann Landers
Feb 19 '07 #9

P: n/a
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);

I didn't get that. Can you please explain a little more?

strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.

Chapter and verse, please.

There is nothing at all in the standard that states that strtod() or
the other related strto... functions do not write to the pointer
addressed by their second argument until after they have finished all
use of the first argument.
C uses pass by value. If you pass the value of p as the first
argument and the address of p as the second argument, these values
are determined at the sequence point after argument evaluation but
before the call of the function. The value of the first argument
(pointer) cannot be changed by subsequent alteration of the pointer
pointed at by the second argument.

strtod() may read the value of the string pointed at by its first
argument. strtod() may modify the pointer pointed at by its second
argument, but it is not supposed to read the string originally
pointed at by the character pointer pointed at by the second argument.
(You're allowed to pass &p for an uninitialized pointer p). Since
there is no overlap between what's read through the first argument
and what's read or written through the second, there's no problem.
>You may say that were you to implement such a function, you would not
store the pointer value until you were finished with all other
processing. You may say that there is no need for any reasonable
implementation of strtod() to do so.
It may do so, and it doesn't matter.
>But unless you can point to wording in the standard that states this,
your answer is wrong and the OP's code produces undefined behavior.

There is absolutely nothing at all preventing an implementation from
providing a strtod() function like this:

double strtod(const char * restrict nptr, char ** restrict endptr)
{
double result = 0.0;
*endptr = NULL;
The above statement cannot mess up nptr nor what it points at.
>
while (*nptr++ != '\0')
{
/* processing */
}
*endptr = nptr;
}
Feb 19 '07 #10

P: n/a
On Feb 18, 11:54 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?

strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.
I still don't understand fully. Can you (or someone else) please give
an example to illustrate this?

Please excuse me for my ignorance.

Feb 19 '07 #11

P: n/a
Gordon Burditt wrote:
>
>>>>You are allowed to do this as long as you bypass any possible
macro definition of strtod:
>
value = (strtod) (p, &p);

I didn't get that. Can you please explain a little more?

strtod(p, &p) both reads from p and writes to p. If you're using
a function, there's no problem, because the reading from p is
guaranteed to take place before any write. If you're using a
macro, the macro is allowed to write to p before reading its
original value.

Chapter and verse, please.

There is nothing at all in the standard that states that strtod()
or the other related strto... functions do not write to the
pointer addressed by their second argument until after they have
finished all use of the first argument.
.... snip ...
>>
There is absolutely nothing at all preventing an implementation
from providing a strtod() function like this:

double strtod(const char * restrict nptr, char ** restrict endptr)
{
double result = 0.0;
*endptr = NULL;

The above statement cannot mess up nptr nor what it points at.
However something like (never mind if it is an accurate strtod)

double strtod(const char *restrict nptr, char* *restrict endptr)
{
if (*nptr) {
do {
**nptr = '0';
} while (*(*nptr)++ != '\0');
}
return 0.0;
}

can foul everything up nicely as a result of ignoring restrict.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
<http://www.securityfocus.com/columnists/423>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
Feb 19 '07 #12

P: n/a
coder wrote:
On Feb 18, 11:54 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?
strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.

I still don't understand fully. Can you (or someone else) please give
an example to illustrate this?

Please excuse me for my ignorance.
Let's use strtol as another example for simplicity, and let's say the
compiler supports GCC-style statement-expressions and C++-style
references as extensions. Then, strtol might be implemented as
something similar to

#define strtol(n, end, base) \
({ \
const char * const &__n = n; \
char * &__end = *end; \
int const &__base = base; \
for (__end = __n; *__end != '\0'; __end++) \
if (!isspace(*__end)) \
break; \
for (; *__end != '\0'; __end++) \
if (!isbasedigit(*__end, __base)) \
break; \
strntol(__n, __end - __n, __base); \
})

Consider what would happen when end == &n: the final call to strntol
(a non-standard helper function) would pass unexpected arguments.

(Note: this example would need some editing to even be a potential
valid implementation of strtol, but the basic idea stands.)

Feb 19 '07 #13

P: n/a
CBFalconer wrote:
Gordon Burditt wrote:
>>>You are allowed to do this as long as you bypass any possible
macro definition of strtod:

value = (strtod) (p, &p);

I didn't get that. Can you please explain a little more?

strtod(p, &p) both reads from p and writes to p. If you're using
a function, there's no problem, because the reading from p is
guaranteed to take place before any write. If you're using a
macro, the macro is allowed to write to p before reading its
original value.

Chapter and verse, please.

There is nothing at all in the standard that states that strtod()
or the other related strto... functions do not write to the
pointer addressed by their second argument until after they have
finished all use of the first argument.
... snip ...
>
There is absolutely nothing at all preventing an implementation
from providing a strtod() function like this:

double strtod(const char * restrict nptr, char ** restrict endptr)
{
double result = 0.0;
*endptr = NULL;
The above statement cannot mess up nptr nor what it points at.

However something like (never mind if it is an accurate strtod)

double strtod(const char *restrict nptr, char* *restrict endptr)
{
if (*nptr) {
do {
**nptr = '0';
} while (*(*nptr)++ != '\0');
}
return 0.0;
}

can foul everything up nicely as a result of ignoring restrict.
How can you assign to **nptr when nptr is a pointer to a character?

Feb 19 '07 #14

P: n/a
>>>>>You are allowed to do this as long as you bypass any possible
>>>>>macro definition of strtod:
>>
>value = (strtod) (p, &p);
>
I didn't get that. Can you please explain a little more?

strtod(p, &p) both reads from p and writes to p. If you're using
a function, there's no problem, because the reading from p is
guaranteed to take place before any write. If you're using a
macro, the macro is allowed to write to p before reading its
original value.

Chapter and verse, please.

There is nothing at all in the standard that states that strtod()
or the other related strto... functions do not write to the
pointer addressed by their second argument until after they have
finished all use of the first argument.
... snip ...
>>>
There is absolutely nothing at all preventing an implementation
from providing a strtod() function like this:

double strtod(const char * restrict nptr, char ** restrict endptr)
{
double result = 0.0;
*endptr = NULL;

The above statement cannot mess up nptr nor what it points at.

However something like (never mind if it is an accurate strtod)

double strtod(const char *restrict nptr, char* *restrict endptr)
{
if (*nptr) {
do {
**nptr = '0';
How can you double-dereference a const char *restrict pointer?
This shouldn't even compile.
} while (*(*nptr)++ != '\0');
}
return 0.0;
}
If you really meant endptr rather than nptr in the above code,
you are dereferencing a pointer (*endptr) that is permitted to
be uninitialized and is supposed to be write-only.
>can foul everything up nicely as a result of ignoring restrict.
It's got a lot more problems than just ignoring restrict.

Feb 19 '07 #15

P: n/a
On Feb 19, 12:15 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 11:54 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?
strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.
I still don't understand fully. Can you (or someone else) please give
an example to illustrate this?
Please excuse me for my ignorance.

Let's use strtol as another example for simplicity, and let's say the
compiler supports GCC-style statement-expressions and C++-style
references as extensions. Then, strtol might be implemented as
something similar to

#define strtol(n, end, base) \
({ \
const char * const &__n = n; \
char * &__end = *end; \
int const &__base = base; \
for (__end = __n; *__end != '\0'; __end++) \
if (!isspace(*__end)) \
break; \
for (; *__end != '\0'; __end++) \
if (!isbasedigit(*__end, __base)) \
break; \
strntol(__n, __end - __n, __base); \
})

Consider what would happen when end == &n: the final call to strntol
(a non-standard helper function) would pass unexpected arguments.

(Note: this example would need some editing to even be a potential
valid implementation of strtol, but the basic idea stands.)
Ok, so the basic idea is that a macro can access variables directly,
whereas variables are passed by value to functions. Is that right?

Feb 19 '07 #16

P: n/a
coder wrote:
On Feb 19, 12:15 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 11:54 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possible macro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?
strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.
I still don't understand fully. Can you (or someone else) please give
an example to illustrate this?
Please excuse me for my ignorance.
Let's use strtol as another example for simplicity, and let's say the
compiler supports GCC-style statement-expressions and C++-style
references as extensions. Then, strtol might be implemented as
something similar to

#define strtol(n, end, base) \
({ \
const char * const &__n = n; \
char * &__end = *end; \
int const &__base = base; \
for (__end = __n; *__end != '\0'; __end++) \
if (!isspace(*__end)) \
break; \
for (; *__end != '\0'; __end++) \
if (!isbasedigit(*__end, __base)) \
break; \
strntol(__n, __end - __n, __base); \
})

Consider what would happen when end == &n: the final call to strntol
(a non-standard helper function) would pass unexpected arguments.

(Note: this example would need some editing to even be a potential
valid implementation of strtol, but the basic idea stands.)

Ok, so the basic idea is that a macro can access variables directly,
whereas variables are passed by value to functions. Is that right?
That may be a bit oversimplified, but it's the basic idea, yes.

Feb 19 '07 #17

P: n/a
On Feb 19, 1:54 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 19, 12:15 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 11:54 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
On Feb 18, 10:47 pm, "Harald van Dijk" <true...@gmail.comwrote:
coder wrote:
<snip>
Thanks for the reply, Harald.
You are allowed to do this as long as you bypass any possiblemacro
definition of strtod:
value = (strtod) (p, &p);
I didn't get that. Can you please explain a little more?
strtod(p, &p) both reads from p and writes to p. If you're using a
function, there's no problem, because the reading from p is guaranteed
to take place before any write. If you're using a macro, the macro is
allowed to write to p before reading its original value.
I still don't understand fully. Can you (or someone else) please give
an example to illustrate this?
Please excuse me for my ignorance.
Let's use strtol as another example for simplicity, and let's say the
compiler supports GCC-style statement-expressions and C++-style
references as extensions. Then, strtol might be implemented as
something similar to
#define strtol(n, end, base) \
({ \
const char * const &__n = n; \
char * &__end = *end; \
int const &__base = base; \
for (__end = __n; *__end != '\0'; __end++) \
if (!isspace(*__end)) \
break; \
for (; *__end != '\0'; __end++) \
if (!isbasedigit(*__end, __base)) \
break; \
strntol(__n, __end - __n, __base); \
})
Consider what would happen when end == &n: the final call to strntol
(a non-standard helper function) would pass unexpected arguments.
(Note: this example would need some editing to even be a potential
valid implementation of strtol, but the basic idea stands.)
Ok, so the basic idea is that a macro can access variables directly,
whereas variables are passed by value to functions. Is that right?

That may be a bit oversimplified, but it's the basic idea, yes.
Thanks for the information, Harald.

Feb 19 '07 #18

P: n/a
Harald van D?k wrote:
CBFalconer wrote:
>Gordon Burditt wrote:
>>>
>>You are allowed to do this as long as you bypass any possible
>>macro definition of strtod:
>>>
>>value = (strtod) (p, &p);
>>
>I didn't get that. Can you please explain a little more?
>
strtod(p, &p) both reads from p and writes to p. If you're using
a function, there's no problem, because the reading from p is
guaranteed to take place before any write. If you're using a
macro, the macro is allowed to write to p before reading its
original value.

Chapter and verse, please.

There is nothing at all in the standard that states that strtod()
or the other related strto... functions do not write to the
pointer addressed by their second argument until after they have
finished all use of the first argument.
... snip ...
>>>>
There is absolutely nothing at all preventing an implementation
from providing a strtod() function like this:

double strtod(const char * restrict nptr, char ** restrict endptr)
{
double result = 0.0;
*endptr = NULL;

The above statement cannot mess up nptr nor what it points at.

However something like (never mind if it is an accurate strtod)

double strtod(const char *restrict nptr, char* *restrict endptr)
{
if (*nptr) {
do {
**nptr = '0';
} while (*(*nptr)++ != '\0');
}
return 0.0;
}

can foul everything up nicely as a result of ignoring restrict.

How can you assign to **nptr when nptr is a pointer to a character?
Woops. Replace nptr with endptr in the above fragment body.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
<http://www.securityfocus.com/columnists/423>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
Feb 20 '07 #19

This discussion thread is closed

Replies have been disabled for this discussion.