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:
- On a platform where type
int
is 16 bits, this wrongly declaresmain
's return type, and the type of theargc
argument, as a 32-bit type. - On a platform where type
char
is unsigned, this wrongly declaresargv
as an array of signed character pointers. That's probably what gcc was complaining about for you. - On a more philosophical level,
main
is not a function whose function signature you get to pick. Somebody else declaredmain
, somebody else is callingmain
, your job is only to provide a definition formain
. 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 ifint
has 32 bits and ifchar
is signed becausechar
andsigned char
are different yet compatible types butchar *
andsigned char *
are incompatible types.if
int
is not the same asint32_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 typeint
is not exactly the same asint
. 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.