[comp.lang.c] an elementary question concerning double indirection

kaires@hubcap.clemson.edu (Robert G Kaires) (02/25/90)

Dear C users:

Thanks to all who answered my question entitled "A very elementary
question concerning indirection". Your answers were most helpful (and
plentiful). Sorry I could answer each response individually.
This next question is maybe not so elementary (hence I left off the
"very"). I am trying to write a code fragment which gets a floating
point number from the user and checks to see if it contains any illegal
characters. The atof() function can change the input string to a
floating point number and stops converting at the first illegal
character. Searching through my Turbo C manual (2.0), I find the
function strtod() which seems to be better suited for my purpose. This
function not only stops at the first illegal character but also returns
a pointer (or is it a pointer to a pointer?). I don't really understand
why this function has the syntax:
             double strtod(const char *s, char **endptr);

Why is endptr a pointer to a pointer (and not just a pointer)?
Unfortunately the description of double indirection is poorly explained
in my manuals, and no description of how to use this function is given.
However, I plunged ahead and tried to use it anyway. Here is my short 
program:

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

main()
{
char string[30]={""};
char **ptr;
double ans;
clrscr();
while(1)
   {
   gets(string);
   if (*string == 'q') break;
   ans=strtod(string,ptr);
   if ( ( string+strlen(string) ) != *ptr )   /* <--- warning here  */
      printf("format error\n");
   else
      printf("You typed the number: %f\n",ans);
   }
}
This program seems to work OK (try it). However I do get warnings on the
line indicated that "I am trying to use ptr before it is defined". Even
worst, when I use this fragment in a much larger program, all hell
breaks loose. It seems that I am writing over sections of memory that
belong to other varaiables. Please tell me what is wrong (without too
much scolding PLEASE, I am still a beginner).
Thanks one and all.
Bob Kaires 

john@stat.tamu.edu (John S. Price) (02/25/90)

In article <8146@hubcap.clemson.edu> kaires@hubcap.clemson.edu (Robert G Kaires) writes:
>Dear C users:
>
>[...stuff deleted...]
>why this function has the syntax:
>             double strtod(const char *s, char **endptr);
>
>Why is endptr a pointer to a pointer (and not just a pointer)?
The reason is this:  you send this function the address of a character pointer,
and it changes the pointer to point to the place in the string where the
error occurs.  For example:

...
char *error, *string = "10.2#4";  /* or whatever you like */
double num;

num = strtod(string, &error);
...

Pass the address of the pointer that you want to point to the error.
That's is why it's declared as a char **.  You have a character pointer,
and you are taking the address, thus a pointer to a character pointer, which
is a char **.

>Unfortunately the description of double indirection is poorly explained
>in my manuals, and no description of how to use this function is given.

There is nothing special about double indirection, although I have
to admit that I was a little confused when I first started working
with it.  Just think of it this way.  When you apply the indirection
operator (*foo), you are looking at what "foo" points to.  This can
be any valid data type, that being an integer, float, structure, or
pointer.  If it's a pointer, well, you dereference it again to get
what it's pointing to.  It's actually quite simple once you catch
on to it.  I've had programs where I've had up to 5 levels of indirection,
something like a pointer to an array of pointers to functions returning
a pointer to a structure, or something like that. 

>However, I plunged ahead and tried to use it anyway. Here is my short 
>program:
>
>#include <math.h>
>#include <stdio.h>
>#include <string.h>
>#include <stdlib.h>
>
>main()
>{
>char string[30]={""};
>char **ptr;
>double ans;
>clrscr();
>while(1)
>   {
>   gets(string);
>   if (*string == 'q') break;
>   ans=strtod(string,ptr);
>   if ( ( string+strlen(string) ) != *ptr )   /* <--- warning here  */
>      printf("format error\n");
>   else
>      printf("You typed the number: %f\n",ans);
>   }
>}
Just change the "char **ptr" to a "char *ptr", and change the
call to strtod(string, ptr) to strtod(string,&ptr).
That SHOULD work, although I haven't tested it.

Hope that made sense, and helps any.

>Bob Kaires 


--------------------------------------------------------------------------
John Price                   |   It infuriates me to be wrong
john@stat.tamu.edu           |   when I know I'm right....
--------------------------------------------------------------------------

raw@math.arizona.edu (Rich Walters) (02/27/90)

In article <8146@hubcap.clemson.edu> kaires@hubcap.clemson.edu (Robert G Kaires) writes:
> this function has the syntax:
>             double strtod(const char *s, char **endptr);
>
>Why is endptr a pointer to a pointer (and not just a pointer)?
>Unfortunately the description of double indirection is poorly explained
>in my manuals, and no description of how to use this function is given.
>However, I plunged ahead and tried to use it anyway. Here is my short 
>program:
>
>char string[30]={""};
>char **ptr;
>double ans;
>
>while(1)
>   {
>   gets(string);
>   if (*string == 'q') break;
>   ans=strtod(string,ptr);
>   if ( ( string+strlen(string) ) != *ptr )   /* <--- warning here  */
>      printf("format error\n");
>   else
>      printf("You typed the number: %f\n",ans);
>   }
>}
>This program seems to work OK (try it). However I do get warnings on the
>line indicated that "I am trying to use ptr before it is defined". Even
>worst, when I use this fragment in a much larger program, all hell
>breaks loose. It seems that I am writing over sections of memory that
>belong to other varaiables. Please tell me what is wrong.

The reason you get the warning is obvious.  ptr has not been (explicity) 
assigned a value.  You will get this warning whenever a variable is initialized
in a standard function.

A better way might be

#include <stdio.h>
/* other appropriate includes */

main()
{
  char string[30];
  char *ptr;
  double ans;
  extern double strtod();
  
  while(1) {
    gets(string);
    if (*string == 'q') break;
    ans=strtod(string,&ptr);
    if ( ( string+strlen(string) ) != ptr )   /* <--- warning here  */
      printf("format error\n");
    else
      printf("You typed the number: %f\n",ans);
  }
}


This works.  strtod wants to modify ptr to point to the correct place.  thus
you must send it the address of (a pointer to) the pointer that is to point to
the terminating character.  On my machine if I send it 12.34abcde, ptr points
to the 'b'. I'm not sure why.


				Richard Walter

-------------------------------------------------------------------------------

			Keep on crunching those numbers

-------------------------------------------------------------------------------

martin@mwtech.UUCP (Martin Weitzel) (02/28/90)

In article <8146@hubcap.clemson.edu> kaires@hubcap.clemson.edu (Robert G Kaires) writes:
> this function has the syntax:
>             double strtod(const char *s, char **endptr);
>
>Why is endptr a pointer to a pointer (and not just a pointer)?
>Unfortunately the description of double indirection is poorly explained
>in my manuals, and no description of how to use this function is given.

Yes, this is a constant source of trouble for novice C programmers.

	********************************************************
NOTE:   ** If you find a double pointer as formal argument    **
	** in the description of some library function, this  **
	** often means you have to supply the ADRESS of a     **
	** (single) pointer variable! Furthermore pointers    **
	** to C-struct-s as formal arguments often means you  **
	** have to supply the adress of a struct-variable.    **
	** Furthermore pointers to char (char *) often means, **
	** that an "array of char" (char foo[SIZE]) would be  **
	** appropriate, when the function is called (%).      **
	********************************************************

Please, do NOT take the above as general rule (eg there may well be
situations where a double pointer formal argument needs a double
pointer as actual parameter a.s.o.), but as a "warning light" which
is switched on in your brains, if you read the manual!

(%): If the library function *writes* to the location, the
pointer points to, make sure "SIZE" is sufficient (and don't
forget the terminating '\0'-Byte!).

[the correct answer for the presented problem was
 allready given by another poster, I'll delete it]
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83