Home > Net >  Can I use int8_t instead of char?
Can I use int8_t instead of char?

Time:12-22

I made a short code like below.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
 
int32_t main(int32_t argc, int8_t *argv[])
{
   int32_t i;
 
   if (argc < 1)
   {
      printf("Error\n");
   }
 
   for (i = 0 ; i < argc ; i  )
   {
      printf("argv[%d] = %s\n", i, argv[i]);
   }
 
   return 0;
}

And compiled like below, then I see a warning like below.

$ gcc -W -Wall main.c
main.c:6:9: warning: second argument of ‘main’ should be ‘char **’ [-Wmain]
 int32_t main(int32_t argc, int8_t *argv[])

What is the best practice to use int8_t?

CodePudding user response:

At its root I believe you're asking a style question, which means you're unlikely to get a definitive answer. Opinions on style are, well, opinions.

Some people believe that you should use C's "natural" types — char, short, int, long, and their unsigned variants — most of the time, and that you should use exact-size types like int32_t only when you absolutely have to.

Some people believe that the variability implied by the "natural" types is an unrelenting source of bugs, and they believe that you should use exact-size types always.

Now, with that said, the specific case of writing

int32_t main(int32_t argc, int8_t *argv[])

is objectively wrong, for at least three reasons:

  1. On a platform where type int is 16 bits, this wrongly declares main's return type, and the type of the argc argument, as a 32-bit type.
  2. On a platform where type char is unsigned, this wrongly declares argv as an array of signed character pointers. That's probably what gcc was complaining about for you.
  3. On a more philosophical level, main is not a function whose function signature you get to pick. Somebody else declared main, somebody else is calling main, your job is only to provide a definition for main. So you simply have to use the types somebody else specified, even if your rule is that you want to use exact-size types whenever you can. Here, you can't.

Bottom line: Please use one of these two (equivalent) forms for main with arguments:

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

int main(int argc, char **argv)

Unless you're writing "freestanding" code, anything else is confusing, misleading, nonstandard, or wrong. (It's also acceptable to define a main that takes no arguments.)

What is the best practice to use int8_t?

I would say, when you really, really need a tiny, 8-bit, signed integer, or perhaps when you're manipulating memory as signed bytes. But I would not go using int8_t instead of char everywhere, because it's going to cause you lots of problems, and it's not going to buy you anything.

CodePudding user response:

What you have posted is an implementation-defined form of main(). It is only allowed in two cases:

  • Either it is 100% compatible with int main (int argc, char** argv), or
  • It is an implementation-defined form that the compiler documentation have told you is fine to use.

It is the C standard and the compiler which decide the acceptable forms of main(), never the programmer.

Notably int8_t may or may not be compatible with char, since char has implementation-defined signedness.

CodePudding user response:

What is the best practice to use int8_t?

When you need a very small signed integer. That's not the same as char. char comes in three flavours:

signed char
unsigned char
char

If you know you want a signed int8_t, use it. If you are dealing with standard string API:s, like main, use char.

int8_t doesn't even need to exist. It's implementation specific. There are platforms where it doesn't exist.

The same goes for int32_t that you use instead of int. It doesn't necessarily exist and even if it does, it's not always a typedef for int - so use int if you want to stay portable.

CodePudding user response:

Your approach has many drawbacks:

  • the prototype for main() is not compatible with any of the standard ones, even if int has 32 bits and if char is signed because char and signed char are different yet compatible types but char * and signed char * are incompatible types.

  • if int is not the same as int32_t, the prototype is definitely incompatible with the standard one and the behavior is undefined.

  • printf("argv[%d] = %s\n", i, argv[i]); would have undefined behavior if type int is not exactly the same as int. You could use the macros from <inttypes.h>, but they are rather cumbersome and would be the code less readable.

Hence the prototype for your main function should be int main(int argc, char *argv[]) and for consistency, i should be defined with the same type as argc: int.

The resulting code is quite simple and readable:

#include <stdio.h>
 
int main(int argc, char *argv[]) {
    int i;
 
    if (argc < 1) {
        printf("Error\n");
    }
 
    for (i = 0; i < argc; i  ) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
 
    return 0;
}

I would argue that best practice regarding int8_t vs char is to use standard prototypes unchanged for main and all library functions. It is also advisable to use char for actual characters used for text as opposed to int8_t and uint8_t for signed and unsigned bytes read from binary contents. String literals should be considered const and manipulated via const char *. Code should behave in a defined manner regardless of the signedness of the char type. This is not just a question of style, it is a sane habit to improve code readability and sturdiness, avoid confusion and some mistakes.

  • Related