Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c: strtod: double pointer vs. reference to single pointer

Tags:

c

pointers

Works great.

#include <stdio.h>
#include <stdlib.h>
int main(void){
  char number[]= "a123.45", *strtod_eptr;
  double num;

  num=strtod(number, &strtod_eptr);
  if (strtod_eptr == number){
    printf("Error: no number found.\n");
  }
  else{ printf("%f\n", num+7);
  }

  return 0;
 }

Does not work. Second argument in strtod() changed type.

#include <stdio.h>
#include <stdlib.h>
int main(void){
    char number[]= "a123.45", **strtod_epptr;
    double num;

    num=strtod(number, strtod_epptr);
    if (*strtod_epptr == number){
        printf("Error: no number found.\n");
    }
    else{
        printf("%f\n", num+7);
    }

return 0;
}

Compiler warns about uninitialized strtod_epptr but no compile errors.

strol_test.c:7:5: warning: ‘strtod_epptr’ is used uninitialized in this function [-Wuninitialized]

Program crashes (seg fault) at the if() statement.

Compiler command (in both cases):

gcc -Wall -pedantic -o "strol_test" "strol_test.c"

Why does this happen? Why does gcc complains about uninitialized **strtod_epptr while being totaly fine with (similarily uninitialized ?) *strtod_eptr ? And what goes wrong when dereferencing **strtod_epptr? AFAIK:

char *ptr;
...strtod(...,&ptr)

should be the same as

char **ptr;
...strtod(...,ptr)

but obviously it is not. What am I missing?

like image 503
K. Nick Avatar asked Jan 17 '26 13:01

K. Nick


2 Answers

Think about memory.

When you write char *str you asked the compiler to allocate memory for a char pointer. Now when you send the address of str to a function, as &str that value inside str can be changed.

Now the other way around.

When you write char **str you asked the compiler to allocate memory for a pointer to a char pointer. That means there is NO memory allocation for the char pointer. If you dereference that pointer now, it will point to nothing meaningful.

Now, you passed str to strtod(). The function now does *str = something(). BUT that place does not exist in the memory. That bucket was not created and allocated.

In the future, always pass the ADDRESS of an already existing variable. If not, there is no variable the function can update...

like image 123
ATE Avatar answered Jan 20 '26 02:01

ATE


strtod expects a double-pointer (pointer to pointer), because it wants to tell the user the location where it stopped reading. So it has to change where a pointer is pointing, thus it cannot take a (single) pointer, it has to take a pointer to a pointer.

man strtod

#include <stdlib.h>
double strtod(const char *nptr, char **endptr);

[...]

RETURN VALUE

These functions return the converted value, if any.

If endptr is not NULL, a pointer to the character after the last character used in the conversion is stored in the location referenced by endptr.

That means that the pointer you pass through endptr must point to a valid char-pointer, that's why

char number[]= "a123.45", *strtod_eptr;
num=strtod(number, &strtod_eptr);

works well because &strtod_eptr returns the address of the strtod_eptr variable, it returns a pointer to a pointer. strtod can then use the pointer to a pointer to change where the original pointer (strtod_eptr) will point.

Internally strtod will do something like this:

double strtod(const char *nptr, char **endptr)
{
    size_t i;
    double converted_value;
    ...
    if(endptr != NULL)
    {
        // i is the index the character after the last
        // character used in the conversion
        *endptr = &(nptr[i]);
    }

    return converted_value;
}

When endptr points to a valid location, dereferencing it won't be a problem.

However

char number[]= "a123.45", **strtod_eptr;
num=strtod(number, strtod_eptr);

doesn't work, because strtod_eptr is an uninitialzed pointer that points to nowhere in particular. When strtod does *endptr = &(nptr[i]), it is trying to write a value in an undefined memory location, this is undefined behaviour and the segfault is a manifestation of that.

This would work however:

char number[]= "a123.45", *end, **strtod_eptr;

strtod_eptr = &end;
num=strtod(number, strtod_eptr);

Because in this case strtod_eptr would point to a valid location.

like image 37
Pablo Avatar answered Jan 20 '26 02:01

Pablo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!