Are there better ways to ease this warning? - c

I have a struct where I use bitfields to optimize memory. I have a uint64_t type and I want to print its value. When compiled it shows me this warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 5 has type ‘long unsigned int:48’ [-Wformat=]
I have already tried to supress this warning by typing while compiling -Wno-format. I'm wondering if there are a better way to do it.
Here some code:
#include <stdint.h>
#include <stdio.h>
typedef struct gn_addr
{
unsigned int m:1;
unsigned int st:5;
unsigned int reserved:10;
uint64_t mid:48;
} gn_addr ;
void gn_addr__print(gn_addr *self)
{
printf("M=>%d\nST=>%d\nReserved=>%d\nMID=>%lu\nTotal size %ld bytes\n",
self->m, self->st, self->reserved, self->mid, sizeof(self));
}

While you should definitely apply the fixes in the other answers to get portable format specifiers, the warning will persist. The reason is that extra arguments to a variadic function like printf undergo argument promotions. Argument promotions include integer promotion.
The rules for integer promotions will convert any integer with a conversion rank less than int/unsigned, as well as bit-fields, into the an int/unsigned. So for your initial bit-fields, you get int automatically.
For integers with a higher conversion rank than int/unsigned, no promotion occurs. So your bit-field is not promoted to an uint64_t, and you get a warning about argument mismatch. You need a cast.
(uint64_t)self->mid
Btw, since nobody mentioned, the portable format specifier for a size_t (the type of the sizeof operator) is %zu. You should use that instead of %ld.

First, use proper format specifier for uint64_t and size_t types, use PRIu64 macro, as defined in inttypes.h and %zu.
That said, for the bit field variables,
you can either use a cast, like (uint64_t)self->mid
or you can use an intermediate variable of type uint64_t, assign the member variable value to the new intermediate variable, and pass that to printf() as the corresponding argument to the format specifier, like
uint64_t temp = self->mid;
printf("%"PRIu64 "\n", temp);

Functions like printf are being examined by compilers (not all). The compiler checks if format specifier matches with the type of argument passed. You need to make explicit conversion to make that Warning go away. As mentioned in other answers that there are some implicit integer promotion rules and you need to be aware of them (Thanks to #storyTeller).
The rules for integer promotions will convert any integer with a conversion rank less than int/unsigned,as well as bit-fields, into the an int/unsigned. So for your initial bit-fields, you get unsigned int automatically.
For integers with a higher conversion rank than int/unsigned, no promotion occurs. So your bit-field is not promoted to an uint64_t, and you get a warning about argument mismatch. You need a cast.
Try this:
(uint64_t)self->mid
I have already tried to supress this warning by typing while compiling -Wno-format. I'm wondering if there are a better way to do it.
It's not a good idea to suppress your warnings. They are generated for a reason. I would recommend to use warning flags like -Wall, -Wshadow, -Wextra and other important flags. They can help you with deploying code with lesser number of bugs.
Regarding your assumption:
I have a struct where I use bitfields to optimize memory.
I would like to point out that your struct with bitfields won't be that much memory optimized as you might be considering it. There is something called Padding and Data Alignment which may help you further in reducing the memory footprint.
Check this question.

Related

How does assigning a string to int and passing that int to printf prints the string properly?

Why does this work? (i.e how is passing int to printf() results in printing the string)
#include<stdio.h>
int main() {
int n="String";
printf("%s",n);
return 0;
}
warning: initialization makes integer from pointer without a cast [enabled by default]
int n="String";
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s",n);
Output:String
compiler:gcc 4.8.5
In your code,
int n="String"; //conversion of pointer to integer
is highly-implementation dependent note 1
and
printf("%s",n); //passing incompatible type of argument
invokes undefined behavior. note 2
Don't do that.
Moral of the story: The warnings are there for a reason, pay heed to them.
Note 1:
Quoting C11, chapter §6.3.2.3
Any pointer type may be converted to an integer type. Except as previously specified, the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined. [....]
Note 2:
chapter §7.21.6.1
[....] If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.
and for the type of argument for %s format specifier with printf()
s If no l length modifier is present, the argument shall be a pointer to the initial
element of an array of character type. [...]
The behaviour of your program is undefined.
Essentially, you're assigning a const char* to an int, and the printf converts it back. But do regard that as entirely coincidental: you're not allowed to cast unrelated types like that.
C gives you the ability to shoot yourself in the foot.
int type can store 4 bytes numbers on most today's computers (from -2147483647 to 2147483647)
Which means it ""can"" store SOME address as well, only problem is when your address is bigger than 2147483647 it will cause overflow and you will not be able to get the address, (which is very bad for your program obviously)
An address is a number referring to memory space,
Pointers are made to store addresses, they are larger (8 bytes on 64bits systems, 4 bytes on 32bits systems) and they are also unsigned (only positive)
which means, when you affect int n="String"; if the address to "String" is under 2147483647 it wont cause issues and you code will run (DONT DO THAT)
http://www.tutorialspoint.com/c_standard_library/limits_h.htm
now if you think about it, you can guess why there is a 4GB ram limit on 32bit systems
(sorry about possible english mistakes, I m french)
Compiling with options like -Wall -Wextra -Werror -Wint-to-pointer-cast -pedantic (GCC) will show you very quickly that this behaviour should not be relied upon.

Why the gcc compiler allowed assigning value without giving data type?

Actually, I had mistakenly typed the below statement but compiler allowed me to execute the statement without throwing any error. My code is,
unsigned i=3;
Why the gcc allowed assigning value without giving data type? Is that the way what gcc will work?
From C11 standard, chapter §6.7.2, Type specifiers, the list of type specifiers appear as,
...
— int, signed, or signed int
— unsigned, or unsigned int
...
and the "Semantics" says,
Each of the comma-separated multisets designates the same type,...
So, basically, unsigned and unsigned int refers to the same type and can be considered interchangeable.
Same logic applies for int, signed and signed int.
So, to answer your question,
Why the gcc compiler allowed assigning value without giving data type?
unsigned itself is a type-specifier, which is same as unsigned int. So, essentially, the data type is not missing here.
Declaring the variable unsigned is the same as declaring it unsigned int in C. Check this Wikipedia link on C data types to learn more about it.
So, gcc treats it correctly and compiles fine.
As summarized by this page, signed, unsigned, short and long all implicitly declare an int, unless otherwise specified (e.g. long double).

Can int store the base address of string in C?

Why do the code run without error ?
#include <stdio.h>
int main() {
int i="string"; //the base of string can be stored in a character pointer
printf("%s\n",i);
printf("%d",i);
return 0;
}
//compiling on ideone.com language c
OUTPUT:
string
134513984 //some garbage(address of "string")
Please explain if there is some flexibility in the pointer in c. I tried it for c++ which gives error: cannot convert ‘const char*’ to ‘int*’ in initialization
No, you cannot assume this in general. In part, this is because int may not be the same size as char * (in fact, on many 64-bit compilers it will not be the same size).
If you want to store a pointer as an integer, the appropriate type to use is actually intptr_t, from <stdint.h>. This is an integer which is guaranteed to be able to hold a pointer's value.
However, the circumstances when you'd actually want to do this are somewhat rare, and when you do do this you should also include an explicit cast:
intptr_t i=(intptr_t)"string"; //the base of string can be stored in a character pointer
This also complicates printing its value, you'll need to use a macro to be portable:
printf("%"PRIiPTR,i);
To print the original string, you should also cast:
printf("%s", (char *)i);
In general, no: the C standard states that conversions from pointers to integers are implementation defined. Further, this can be problematic on systems where sizeof(char *) and sizeof(int) are different (i.e. x86-64), for two reasons:
int i = "string"; can lose information, if the e.g. 64-bit pointer cannot fit in a 32-bit integer.
printf expects a pointer to be passed in, but gets a smaller integer. It winds up reading some garbage into the full pointer, and can crash your code (or worse).
Often times, however, compilers are "smart" enough to "fix" arguments to printf. Further, you seem to be running on a platform where pointers and integers are the same size, so you got lucky.
If you compiled this program with warnings (which you should) you'd get the following complaints:
main.c:3:9: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'char [7]' [-Wint-conversion]
int i="string"; //the base of string can be stored in a character pointer
^ ~~~~~~~~
main.c:4:19: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
printf("%s\n",i);
~~ ^
%d
2 warnings generated.
Warnings generally mean you're doing something that could cause unexpected results.
Most C compilers will let you do this, but that doesn't make it a good idea. Here, the address of the character array "string" gets stored in i. The printf options are determining how the integer is interpreted (as an address or an integer). This can be problematic when char* is not the same size as an int (e.g. on most 64 bit machines).
The C++ compiler is more picky and won't let you compile code like this. C compilers are much more willing, although they will usually generate warnings letting the programmer know it is a bad idea.
Your code is ill-formed in both C and C++. It is illegal to do
int i = "string";
in both languages. In both languages conversion from a pointer to an integer requires an explicit cast.
The only reason your C compiler accepted it is that it was configured by default for rather loose error checking. (A rather typical situation with C compilers.) Tighten up your C compiler settings and it should issue an error for the above initialization. I.e. you can use an explicit conversion
int i = (int) "string";
with implementation-dependent results, but you can't legally do it implicitly.
In any case, the warning your compiler emitted for the above initialization is already a sufficient form of a diagnostic message for this violation.

C compiler warning error

A simple program
#include<stdio.h>
int main() {
char b='a';
printf("%s \n", b);
return 0;
}
Output:
test.c: In function ‘main’:
test.c:4:1: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("%s \n", b);
^
But the second arg is char not int.
Is this a wrong warning by the compiler, or something else I am missing?
The compiler is certainly correct to warn about the call. printf with a "%s" specifier requires an argument of type char* (which must point to a string), and the argument you pass is not of that type.
As for why the warning message refers to int rather than char, it's because printf is a variadic function. The first parameter, the format string, is declared to be of type const char*, but the following arguments are specified only as , .... In this special case, arguments of integer types narrower than int are promoted to int or to unsigned int. So even though the expression b is of type char, the actual argument that's passed to printf is of type int; specifically, it's the result of converting the value of b from char to int.
The warning is accurate, though the reasons are modestly obscure.
The declaration of printf() is:
int printf(const char *restrict format, ...);
The const and restrict keywords aren't important for this discussion. What is important is the ellipsis, .... When arguments are passed to a function with a variable argument list (a variadic function), they undergo 'default argument conversions'. Integer types shorter than int (short and char in their various forms) are promoted to int, and float values are promoted to double. Thus, the value of b is converted to int by the rules for calling a variadic functions.
The "%s" format expects to be given a pointer to a null-terminated character string. The variable b is a single char, not a string. Consequently, the compiler is correctly warning you that you will not get good results from running the program.
In this context, using a format such as %c (to print a character) or %d (to print a decimal integer) is probably best:
printf("%c\n", b);
As a general rule, at this stage in your C programming career, you should assume the compiler is right and that you're wrong. Remember, the C compiler knows a lot more about C than you do. This isn't to say that there are never bugs in compilers. However, the chances of you finding one are slim. Until you know enough about C (maybe in five to ten years time), then you should assume the compiler is right, you're wrong, and work out (a) what the compiler means and (b) how to fix it.
It was not always thus. Thirty years ago, bad compilers existed because the best were not all that much better. It was possible to find bugs in those compilers. There has, however, been a serious winnowing and few incompetent compilers are left on the market. One area where you can sometimes find compilers with surprising limitations (occasionally tantamount to bugs) is in specialized embedded systems for obscure chips. However, in mainstream o/s for desktops and servers (and tablets and smart phones), you're unlikely to come across a seriously defective compiler.
ISO/IEC 9899:2011 §6.5.2.2 Function calls
¶6 If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions. If the number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that includes a prototype, and
either the prototype ends with an ellipsis (, ...) or the types of the arguments after
promotion are not compatible with the types of the parameters, the behavior is undefined.
If the function is defined with a type that does not include a prototype, and the types of
the arguments after promotion are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following cases:
one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;
both types are pointers to qualified or unqualified versions of a character type or
void.
The 'integer promotions' are defined in §6.3.1.8 Usual arithmetic conversions. They're more complex than I want to go through here.
a char is really just an 8 bit number, so a single char alone is no different than an int, except that it can only store smaller numbers. In your printf statement you have
%s
but a single char is not considered a string, just a number, so you should use
%c or %d
for you printf statement. If you had an array of chars, then you would use %s
Try this instead:
printf("%c \n", b);
The compiler is casting the char to int on the fly, so the error msg makes sense (at least to someone used to interpreting C compiler messages).

What's the correct way to sprintf a float?

I'm trying to printf/sprintf floats using this code:
sprintf(buff, "FPS: %d\n%.4f N %.4f E\nAl: %.1fm Rl: %.1f\n", fps, p_viewer.p.lat, p_viewer.p.lon, p_viewer.p.alt, p_viewer.roll);
However I'm getting these warnings when I do so:
gfx_game_engine.c:300: warning: format '%.4f' expects type 'double', but argument 4 has type 'float'
gfx_game_engine.c:300: warning: format '%.4f' expects type 'double', but argument 5 has type 'float'
gfx_game_engine.c:300: warning: format '%.1f' expects type 'double', but argument 6 has type 'float'
gfx_game_engine.c:300: warning: format '%.1f' expects type 'double', but argument 7 has type 'float'
What's the correct way to sprintf a float? Is there a special format character? I feel that the compiler might be casting the types somehow and this might contribute to it slowing down.
I'm using a dsPIC33FJ128GP802 microcontroller and compiling with MPLAB C30, a variant of GCC v3.23.
Your variant of gcc is broken. In the C language, there is no way to pass a float to a variadic function. Default promotions of small types (char, short, and float) up to at least int/double always apply. I'm guessing whoever hacked gcc for this microcontroller did something to disable promotions, probably with the idea that double is slow or hard to pass. You should flame them to hell and back. The correct solution, if a compiler vendor does not want to support double correctly, is to make all the floating point types the same size and equivalent to float, not to violate the promotion rules of the language.
Another idea: It's also possible that the prototype for sprintf is missing or wrong, for instance int sprintf(); instead of int sprintf(char *, const char *, ...);
float arguments should automatically be converted to double. There seems no other way to print a float. Your compiler shouldn't complain in this case.
Unlike for scanf, where %f means float and %lf means double, printf does not distinguish between them. So if printf (or its variants) are implemented via a library linkage, the compiler will have to convert.

Resources