2

I'm learning C (the hard way) and try to write a function (simple exercise) that prints a line according to argument datatype.

 #include<stdio.h>
 #include<ctype.h>

 int main( int argc,  char *argv[] )
 {
     int i = 0;
     for(i = 0; i < argc; i++){
         if(isalpha((int)argv[i]) )  //this line produces a warning
         {
             printf("I am alpha.\n");
         }
     }

     return 0;
 }

This produces a warning upon compilation (and does nothing upon execution):

cast to smaller integer type 'int' from 'char *' [-Wpointer-to-int-cast]

update after comments I'm comparing pears and apples: string versus int and they are not same amount of bytes. So the question is: how do I write a data comparison if-statement, like in ruby:

a = "5"
if a.instance_of? String
# do something
end
thiebo
  • 1,339
  • 1
  • 17
  • 37
  • What do you intent `isalpha((int)argv[i])` to mean? `argv[i]` is a _string_, while `isalpha` takes _one_ character as argument. – KamilCuk May 19 '21 at 04:46
  • A pointer is 8 bytes, while an int is 4 bytes. So the warning tells you that you lose information in the cast. – Jeppe May 19 '21 at 04:48
  • I thought `strings` didn't exist in c as they are arrays of letters. – thiebo May 19 '21 at 04:54
  • 1
    @thiebo, it is understood by saying strings in c, that they are array of characters. – Siddhant May 19 '21 at 04:56
  • @thiebo You could say that, but `argv[i]` is a pointer to the first element of that array of characters, so how can you check if a pointer is a letter? You could *dereference* `argv[i]` to get the first character in the string, so `argv[i][0]`. – mediocrevegetable1 May 19 '21 at 04:56
  • 3
    @thiebo `argv[i]` is **always** a C-style string. You don't need to check that. A C program can only and will always get command line arguments as strings – Support Ukraine May 19 '21 at 05:01
  • Maybe you're trying to ask the question: how can I know if a string contains only digits? – Jeff Holt May 19 '21 at 05:04
  • @thiebo Are you really trying to test whether a string only contains alphabetic character? – Support Ukraine May 19 '21 at 05:04
  • [§7.1.1 Definition of terms](http://port70.net/~nsz/c/c11/n1570.html#7.1.1): _A string is a contiguous sequence of characters terminated by and including the first null character. The term multibyte string is sometimes used instead to emphasize special processing given to multibyte characters contained in the string or to avoid confusion with a wide string. A pointer to a string is a pointer to its initial (…) character. The length of a string is the number of bytes preceding the null character and the value of a string is the sequence of the values of the contained characters, in order._ – Jonathan Leffler May 19 '21 at 05:05
  • You don't need to check datatypes in C because `void func(int a, char b)`. Here, `a` will always be `int` and `b` will always be a `char`. You know the datatypes at compile time. Same applies to `int main(int argc, char *argv[])`. `argc` is always an `int` and `argv` is always an array of strings. The only time you have a datatype that can be different is with `void *`, but it's just a bunch of memory, C has no way to check the datatype. – Shambhav May 19 '21 at 05:16

4 Answers4

3

write a function that prints a line according to argument datatype

Here you have a fundemental misunderstanding. The (command line) arguments for a C program are always C style strings. Always...

There is no way to get any other data type. So if you want to give your program another input type, e.g. a number, you need to write code that converts a string to a number.

Example:

./myprogram 123
            ^^^
            Start myprogram using the NUMBER 123

Once you get into your main there is no number. Instead you'll get a string, i.e. "123" and argv[1] will point to that string. To get a number, you need code like:

int number = convert_string_to_number(argv[1]);   // Convert the string "123"
                                                  // to the number 123

(note: convert functions are already available - e.g. https://man7.org/linux/man-pages/man3/strtol.3.html)

Further... Your title says "test datatype". In C that's not really something you do. For details you may find this https://stackoverflow.com/a/6280095/4386427 interresting.

BTW:

If all you want to do is to print the arguments simply print all of them as strings:

for(int i = 0; i < argc; ++i)
{
    printf("argument %d is %s\n", i, argv[i]);
                                           
}

BTW 2:

For your warning... As it's already mentioned in several comments, the (int)argv[i] is casting a pointer value into an integer value. The language itself doesn't forbid that but it's something you would never do... so the compiler gives you a warning. Kind of like saying "This looks strange. Sure that is really what you want to do?" And in your case the answer is NO.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
3

As you seem to already know, "strings" in C are \0-terminated arrays of characters. argv is an array of pointers to the beginning of these arrays. So for example, argv[0] is almost always the path of the executable. Let's say you run the program as ./a.out. argv[0] will look like this:

+---+---+---+---+---+---+---+---+
| . | / | a | . | o | u | t | \0|
+---+---+---+---+---+---+---+---+
  ^argv[0] points to this character

So that explains what argv's elements are. I think you understand what's wrong with your program. You're simply trying to get the address held by argv[i] into an int, but this in itself is also clearly wrong since on your system, a pointer is larger than an int so the address can get truncated. From what I can tell, in your program, you either want to:

  1. Check if each element of argv is a string that contains only 1 alphabet.

  2. Check if each element of argv is a string of only alphabets (e.g. "abcde").


  1. Use isalpha on argv[i][0] (first character) and check if the length of the string is only 1 character (and a NUL-terminator):
for (int i = 0; i < argc; ++i)
{
    if (isalpha(argv[i][0]) && argv[i][1] == '\0') // if length is 1 char, \0 will be right after
        puts("I'm a character.");
}
  1. Use isalpha in a loop to check if each character is an alphabet.
for (int i = 0; i < argc; ++i)
{
    _Bool isalphastr = 1;
    for (const char *j = argv[i]; *j != '\0'; ++j)
    {
        if (!isalpha(*j))
        {
            isalphastr = 0;
            break;
        }
    }
    if (isalphastr)
        puts("I'm a string of letters");
}

Alternatively:

#include <string.h>

for (int i = 0; i < argc; ++i)
{
    if (strspn(argv[i], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(argv[i]))
        puts("I'm a string of letters");
}

But I think that string gets just a bit too long...

mediocrevegetable1
  • 4,086
  • 1
  • 11
  • 33
1

As the accepted answer explains pretty well everything but your compiler error I would like to try to provide a proper explanation about it.

argv[i] is a char * (aka char pointer, aka C-string) and in general a pointer is just a size_t (an unsigned integer that has 16 bits or more) specifying the position of something in the RAM.

int is simply a signed integer that has 16 bits or more.

With this code you can find out how many bits your size_t and int has:

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

  printf( "Bits of `char *`: %d", sizeof(char *)*8 );
  printf( "Bits of `int`: %d",    sizeof(int)*8    );

  return 0;
}

Let's say that char * is 32 bits and int is 16 bits: when casting from char * to int the program is going to "cut" the remaining 16 bits.

Please note that the 16 bits of the int that are kept might start from the end or form the start of the 32 ones of size_t depending on you CPU endianess.

DadiBit
  • 769
  • 10
  • 22
0

You are casting from pointer to integer. Instead, try using content operator((int)(*argv[i])).

NeWi-SL
  • 127
  • 1
  • 6