learning C - test datatype

Viewed 130

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
4 Answers

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.

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...

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.

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

Related