2

I have this code:

int main(int argc, char *argv[]) {
    int num = *argv[1];

When I run the function in terminal with a parameter: for example, if I were to call ./main 17, I want num = 17. However, with this code, num = 49 (ASCII value for 1 because argv is an array of characters). How would I get it to read num = 17 as an int? Playing around with the code, I can get it to convert the parameter into an int, but it will still only read/convert the first value (1 instead of 17).

I'm new to C and the concept of pointers/pointers to arrays is still confusing to me. Shouldn't *argv[1] return the value of the second char in the array? Why does it read the first value of the second char in the array instead?

Thanks for help!

Andre Kampling
  • 5,476
  • 2
  • 20
  • 47
  • `argv[1]` is a pointer to `char` holding the 1st whole argument. – JFMR Aug 15 '17 at 06:03
  • 5
    `if (argc > 1) { int num = atoi(argv[1]); }` – kocica Aug 15 '17 at 06:06
  • 1
    The arguments are strings. You need to convert them to numbers, perhaps with `atoi()` or one of its relatives, or perhaps with `strtol()` or one of its relatives. The latter requires considerable care to get any benefits from it. – Jonathan Leffler Aug 15 '17 at 06:09
  • 2
    @FilipKočica [Why not to use atoi](https://stackoverflow.com/questions/17710018/why-shouldnt-i-use-atoi) – Tony Tannous Aug 15 '17 at 06:16
  • For information on the 'considerable care' required to manage `strtol()`, see [Correct usage of `strtol()`](https://stackoverflow.com/questions/14176123/correct-usage-of-strtol). – Jonathan Leffler Aug 15 '17 at 14:50

4 Answers4

2

Shouldn't *argv[1] return the value of the second char in the array?

Look at the signature:

int main(int argc, char *argv[])

Here, argv is an array ([]) of pointers (*) to char. So argv[1] is the second pointer in this array. It points to the first argument given at the command line. argv[0] is reserved for the name of the program itself. Although this can also be any string, the name of the program is put there by convention (shells do this).

If you just dereference a pointer, you get the value it points to, so *argv[1] will give you the first character of the first argument. You could write it as argv[1][0], they're equivalent. To get the second character of the first argument, you'd write argv[1][1].


An important thing to note here is that you can never pass an array to a function in C. The signature above shows an array type, but C automatically adjusts array types to pointer types in function declarations. This results in the following declaration:

int main(int argc, char **argv)

The indexing operator ([]) in C works in terms of pointer arithmetics: a[x] is equivalent to *(a+x). The identifier of an array is evaluated as a pointer to the first array element in most contexts (exceptions include the sizeof operator). Therefore indexing works the same, no matter whether a is an array or a pointer. That's why you can treat argv very similar to an array.


Addressing your "core" problem: You will always have strings in argv and you want numeric input, this means you have to convert a string to a number. There are already functions doing this. A very simple one is atoi(), you can use it like this:

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

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        // use program name in argv[0] for error message:
        fprintf(stderr, "Usage: %s [number]\n", argv[0]);
        return EXIT_FAILURE;
    }
    int i = atoi(argv[1]);
    printf("Argument is %d.\n", i);
    return EXIT_SUCCESS;
}

This will give you 0 if the argument couldn't be parsed as a number and some indeterminate value if it overflows your int. In cases where you have to make sure the argument is a valid integer, you could use strtol() instead (note it converts to long, not int, and it can handle different bases, so we have to pass 10 for decimal):

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

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        // use program name in argv[0] for error message:
        fprintf(stderr, "Usage: %s [number]\n", argv[0]);
        return EXIT_FAILURE;
    }

    errno = 0;    // reset error number
    char *endptr; // this will point to the first character not read by strtol
    long i = strtol(argv[1], &endptr, 10);
    if (errno == ERANGE)
    {
        fprintf(stderr, "This number is too small or too large.\n");
        return EXIT_FAILURE;
    }
    else if (endptr == argv[1])
    {
        // no character was converted. This also catches the case of an empty argument
        fprintf(stderr, "The argument was not a number.\n");
        return EXIT_FAILURE;
    }
    else if (*endptr)
    {
        // endptr doesn't point to NUL, so there were characters not converted
        fprintf(stderr, "Unexpected characters in number.\n");
        return EXIT_FAILURE;
    }

    printf("You entered %ld.\n", i);
    return EXIT_SUCCESS;
}
1

I'm new to C and the concept of pointers/pointers to arrays is still confusing to me.

In C strings are represented by null terminated ('\0') character arrays. Let's consider the following example:

char str[] = "Hello world!"

The characters would lie contiguous in memory and the usage of str would decay to a character pointer (char*) that points to the first element of the string. The address of (&) the first element taken by &str[0] would also point to that address:

| . | . | . | H | e | l | l | o |   | W | o | r | l | d | ! | \0 | . | . | . |
              ^                                               ^
             str                                        null terminator

Shouldn't *argv[1] return the value of the second char in the array?

First of all in the argv is an array of character pointer char* argv[], so that it could be interpreted like an array of strings.

The first string argv[0] is the program name of the program itself and after that the arguments that are passed are coming:

argv[0] contains a pointer to the string:    "program name"
argv[1] contains a pointer to the argument:  "17"

If you dereference argv[1] with the use of * you get the first character at that address, here 1 which is 49 decimal in the Ascii code. Example:

    p             r        ("program name")
    ^             ^
 argv[0]    (argv[0] + 1)
--------------------------------------------
    1             7        ("17")
    ^             ^
 argv[1]    (argv[1] + 1)

How would I get it to read num = 17 as an int?

Check the number of passed arguments with argc which contains also the program name as one (read here more about argc and argv). If there are 2 you can use strtol() to convert argv[1] to the an integer. Use strtol() over atoi() because atoi() is considered to be deprecated because there is no error checking available. If atoi() fails it simply returns 0 as integer instead of strtol() that is setting the second argument and the global errno variable to a specific value.

The followig code will use the pointer that strtol() set the second argument to, to check for conversion errors. There are also overflow and underflow errors to check like it's described here on SO. Moreover you have to check if the returned long value would fit into an int variable if you want to store it into an int variable. But for simplicity I've left that out:

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

int main (int argc, char* argv[])
{
   /* At least 1 argument passed? */
   if (argc >= 2)
   {
      char* endptr;
      long num = strtol(argv[1], &endptr, 10);

      /* Were characters consumed? */
      if (argv[1] != endptr)
      {
         printf("Entered number: %ld\n", num);
      }
      else
      {
         printf("Entered argument was not a number!\n");
      }
   }
   else
   {
      printf("Usage: %s [number]!\n", argv[0]);
   }
   return 0;
}
Andre Kampling
  • 5,476
  • 2
  • 20
  • 47
  • Hi @Amy if this or any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/q/5234/179419) by clicking the check-mark. This indicates to the wider community that you've found a solution and gives some reputation to both the answerer and yourself. There is no obligation to do this. – Andre Kampling Aug 16 '17 at 06:34
1

How do you convert parameters from char to int?

Can be done by a simple cast (promotion), but this isn't your case.

In your case *argv[] is array of pointer to char (You can use this for breaking down complex C declarations), meaning that argv[1] is the 2nd element in the array, i.e. the 2nd char* in the array, meaning *argv[1] is the first char in the 2nd char* in the array.

To show it more clearly, assume argv holds 2 string {"good", "day"}. argv[1] is "day" and *argv[1] is 'd' (note the difference in types - char vs char*!)

Now, you are left with the 1st char in your input string i.e. '1'. Its ascii is indeed 49 as, so in order to get it's "int" value you should use atoi like this:

int i = atoi("17");

BUT atoi gets const char * so providing it with 17 is a good idea while sending it a char would not. This means atoi should get argv[1] instead of *argv[1]

int main(int argc, char *argv[]) {
     int num = atoi(argv[1]);
     // not    : int num = *argv[1];        -->  simple promotion that would take the ascii value of '1' :(
     // and not: int num = atoi(*argv[1]);  -->  argument is char

note: atoi is considered obsolete so you may want to use long int strtol(const char *str, char **endptr, int base) but for a simple example I preferred using atoi

CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
-1

Here's what you want to do:

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

int main (int argc, char *argv []) {
    int num = atoi (argv[1]);
    printf ("Here's what you gave me: %d", num);
    return 0;
}

Here's the documentation for atoi ().

argv is an array of strings, so argv[x] points to a string. atoi () accepts an ASCII string as input and returns an int.

Bonus: This is still a bit unsafe. Try running this program without passing it a parameter and see what happens.

Also, you must take a look at the documentation for strtol (), which is a safe way of doing this.

Prajjwal
  • 1,055
  • 2
  • 10
  • 17