Why is the second argument not working with strtol?

562 Views Asked by At

I did this something like this:

/* convert the argv[1] into Integer and store the result in key
 * using library function: strtol() to do it */
char **flag = NULL;
key = strtol(argv[1], flag, 10);

// if argv[1] is not all digits
if (**flag != '\0')
{
    printf("Usage: ./caesar key\n");
    return 1;
}

But it throws a segmentation fault. I am not sure why.

In the C documentation, strtol is prototyped as long int strtol(const char *nptr, char **endptr, int base). Why am I getting a segmentation fault?

And when change some parts of the code to char *flag, strtol(argv[1], &flag, 10), and if (*flag != '\0'), everything works as expected.

I understand (sort of) how making the edits corrects the code. However, I do not know why the original code does not work. Does anyone have a clue?

4

There are 4 best solutions below

0
alk On BEST ANSWER

I do not know why the original code does not work? Does anyone have any clue?

Because here

char **flag = NULL;

flag is set to NULL, then here

key = strtol(argv[1], flag, 10);

flags's value (NULL) is passed to strtol(), which does not change flags's value in any way, it is NULL before and after the call to strtol().

and finally here

if (**flag != '\0')

flag, which has the value NULL, is dereferenced, which invokes undefined behaviour, which could lead to anything, which in your case is a crash.

2
Jonathon Reinhart On

You need to pass the address of your char * pointer, so that strtol can update it.

char *flag = NULL;
key = strtol(argv[1], &flag, 10);

// if argv[1] is not all digits
if (*flag != '\0')

After the call, the pointer will be modified to point to the end of the parsed region of the input string.

The man page wording is admittedly confusing here.

0
Jabberwocky On

Consider this simple function which multiplies a number by 2.

void Times2(int number, int *result)
{
  *result = number * 2;
}

You should call it like this:

int result;
Times2(3, &result);
printf("%d\n", result);

and the expected output would be 4.

But if you call it like this (which is basically what you're doing when you call strtol in your buggy code):

int *result = NULL;
Times2(3, result);
printf("%d\n", result);

your program will most likely end up with a segmentation fault, because in the Times2 function you dereference a NULL pointer.

0
chux - Reinstate Monica On

Why second argument not working with strol?

char **flag = NULL;
key = strtol(argv[1], flag, 10);

It is working. Passing is a null pointer is fine, but flag will not get changed in the calling code.

When the 2nd argument is a null pointer, nothing is updated about where the conversion ended.


Later code: *flag in if (**flag != '\0') is bad (undefined behavior) as that attempts to de-reference a null pointer.


Instead pass the address of a known pointer:

//char **flag = NULL;
char *flag;

//key = strtol(argv[1], flag, 10);
key = strtol(argv[1], &flag, 10);

// if (**flag != '\0')
if (*flag != '\0') 

Following is a complete test conversion of a char* to an int.

#include <stdlib.h>
#incluse <ctype.h>

// Return error flag
int my_strtol(long *destination, const char *s) {
  if (destination == NULL) {
    return 1;
  } 
  if (s == NULL) {
    *destination = 0;
    return 1;
  }

  char *endptr;
  errno = 0;
  *destination = strtol(s, &endptr, 0); 

  // If no conversion or out-of-range
  if (s == endptr || errno == ERANGE) {
    return 1;
  }
  // I like to tolerate trailing white-space.
  while (isspace(*(const unsigned char *) endptr)) {
    endptr++;
  }
  return *endptr != '\0';
}