What is the need of hh and h format specifiers? - c

In the code below mac_str is char pointer and mac is a uint8_t array:
sscanf(mac_str,"%x:%x:%x:%x:%x:%x",&mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]);
When I try the above code it gives me a warning:
warning: format ‘%x’ expects argument of type ‘unsigned int *’, but argument 8 has type ‘uint8_t *’ [-Wformat]
but I saw in some code they specified
sscanf(str,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",&mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]);
which doesn't give any warning but both are working the same.
What's the need of using hhx instead of just x?

&mac[0] is a pointer to an unsigned char.1 %hhx means the corresponding arguments points to an unsigned char. Use square pegs for square holes: the conversion specifiers in the format string must match the argument types.
1 Actually, &mac[0] is a pointer to a uint8_t, and %hhx is still wrong for uint8_t. It “works” in many implementations because uint8_t is the same as unsigned char in many implementations. But the proper format is "%" SCNx8, as in:
#include <inttypes.h>
…
scanf(mac_str, "%" SCNx8 "… rest of format string", &mac[0], … rest of arguments);

hh is a length modifier that specifies the destination type of the argument. The default for conversion format specifier x is unsigned int*. With hh, it becomes unsigned char* or signed char*.
Refer to the table herein for more details.

hhx converts input to unsigned char, while x converts to unsigned int. And since uint8_t is typedef to unsigned char, hhx fixes warning.

Related

what does mean this exception ? format %d expects argument of type 'int', but argument 2 has type 'long long unsigned int' [-Wformat] [duplicate]

This question already has answers here:
How should I print types like off_t and size_t?
(10 answers)
Closed 2 years ago.
I'm a student trying to learn C and C++, I've got a problem with the specifier %d I dont't understand the exception that is written in the console, It's writing
The format %d expects argument of type 'int', but argument 2 has type 'long long unsigned int' [-Wformat]
Here is the code :
#include<stdio.h>
#include<stdlib.h>
int main()
{
short int u=1;
int v=2;
long int w=3;
char x='x';
float y=4;
double z=5;
long double a=6;
long b=7;
printf("short int:%d\n",sizeof(u));
printf("int:%d octets\n",sizeof(v));
printf("long int:%d octets\n",sizeof(w));
printf("char:%d octets\n",sizeof(x));
printf("float:%d octets\n",sizeof(y));
printf("double:%d octets\n",sizeof(z));
printf("long double:%d octets\n",sizeof(a));
printf("long:%d octets\n",sizeof(b));
return 0;
}
The type of the value returned by the operator sizeof is the unsigned integer type size_t The conversion specifier d is used to output values of the type int. You have to use the conversion specifier zu. Otherwise you will get undefined behavior.
For example
printf("short int:%zu\n",sizeof(u));
From the C Standard (7.21.6.1 The fprintf function)
9 If a conversion specification is invalid, the behavior is
undefined.275) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.
The sizeof operator returns a size_t type. This is always unsigned and, on your platform, is a long long unsigned int; on other platforms, it may be just unsigned long or, indeed, some other (unsigned) integer type.
Use the %zu format specifier for arguments of this type; this will work whatever the actual size_t type definition happens to be.

Promotions and conversions of variables in printf()

In this case,
#include <stdio.h>
int main()
{
unsigned char a = 1;
printf("%hhu", -a);
return 0;
}
The argument -a in printf is promoted to int by the integer promotion by the unary minus operator and subsequently promoted by the default argument promotion and finally converted to unsigned char by the format specifier.
So -a => -(int)a(by ~) => no conversion by function call => (unsigned char)-(int)a(by %hhu). Is my thought right?
You are correct that a is promoted to int in -a, and that printf("%hhu", -a); passes an int to printf. The notional conversion performed with %hhu is not clear.
Note that if a is not zero, then -a produces a value (in an int) that is not an unsigned char value. Further, with two’s complement eight-bit signed char, if a is greater than 128, then -a produces a value that is not a signed char value.
To understand %hhu, we look at the specification for u in C 2018 7.21.6.1 8:
The unsigned int argument is converted to unsigned octal (o), unsigned decimal (u),…
and for hh in 7.21.6.1 7:
Specifies that a following d, i, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument (the argument will have been promoted according to the integer promotions, but its value shall be converted to signed char or unsigned char before printing);…
First we have to resolve this issue of “signed char or unsigned char”. Does this say we can pass either a signed char or an unsigned char for %hhu? I think not; I think the authors have just put together the language for %hhd (intended to convert a signed char) and %hhu (intended to convert an unsigned char). So I believe the intent is that a promoted unsigned char should be passed for the %hhu conversion specification.
Apple Clang 11.0.0 seems to agree, when passing -a (but not a), it warns: “warning: format specifies type 'unsigned char' but the argument has type 'int' [-Wformat]”
As noted above, passing -a may pass a value that cannot result from passing a promoted unsigned char. It may even pass a value that cannot result from passing a promoted signed char or unsigned char. In this case, it can be argued we have violated the requirement to pass an unsigned char, and therefore the C standard does not specify the resulting behavior. Even though it says the passed value shall be converted to an unsigned char, I believe that is a notional conversion, not a specific requirement on the library implementation, and that is also falls under the “as if” rules: It does not actually have to be performed if the resulting defined behavior of programs is the same. But, since passing an improper value may not be defined, we do not have defined behavior.
That may be a strict reading of the rules, but it would not surprise me greatly if printf printed “4294967295” instead of “255” when a were 1.
printf is a variadic function. The type of the arguments passed by ... parameter are not known inside the function. As such, any variadic function must rely on other mechanisms to interpret the type of the va_args arguments. printf and family use a const char* format string to "tell them" what kind of arguments were passed. Passing a type different then the expected type as specified by it's format specifier results in Undefined Behavior.
For instance:
printf("%f", 24)
Is undefined behavior. There is no conversion from int to float anywhere because the arguments are passed as they are (after promotion) and inside the printf the function incorrectly treats its first argument as float. printf does not know and can't know that the real type of the argument is int.
Variadic arguments undergo some promotions of their own. Of interest for your question unsigned char is promoted to int or unsigned int (I am not sure tbo). As such there is no way for a variadic parameter to actually be of type unsigned char. So hhu while is indeed the specifier for unsigned char it will actually expect an unsigned int (int), which is what you pass to it.
So afaik the code is safe because of the two integer promotions caused by unary minus and passing variadic arguments. I am not 100% sure though. Integer promotions are weird and complicated.

How to avoid sprintf warning for uint8_t data type

My questions are based on the snippets below:
[line1] #include <stdio.h>
.
.
.
[line123] uint8_t msg[100];
[line124] memset(msg,0,sizeof(msg));
[line125] sprintf(msg,"SYSCLK : %ld\r\n",HAL_RCC_GetSysClockFreq());
[line126] HAL_UART_Transmit(&huart2,msg,strlen(msg),HAL_MAX_DELAY);
.
.
.
[line130] char msg1[100];
[line131] memset(msg1,0,sizeof(msg1));
[line132] sprintf(msg1,"SYSCLK : %ldHz\r\n",HAL_RCC_GetSysClockFreq());
[line133] HAL_UART_Transmit(&huart2,(uint8_t*)msg1,strlen(msg1),HAL_MAX_DELAY);
Why does line 125 get a warning: " passing argument 1 of 'sprintf'
from incompatible pointer type "
Refer to String Format Specifiers, I know that ...
h is a length modifier that applies to an unsigned short argument,
hh to an unsigned char, and
z to a size_t
...but doubt what %ldHz on line 132 means and why doesn't this line get a warning?
Thanks.
The Hz is not part of the format specifier: it is simply printing the standard abbreviation for "Hertz" after the frequency value. So, the actual format specifier is just %ld, printing a long integer in decimal format.
The first argument to sprintf is a char *. You're passing in a uint8_t * (i.e. an unsigned char *). That's a pointer type mismatch.
The actual format specifier is %ld. The Hz that follows is just literal text that gets printed.
Why does line 125 get a warning: " passing argument 1 of 'sprintf' from incompatible pointer type
uint8_t isn't necessarily directly compatible with char - the latter has implementation-defined signedness. uint8_t can however be safely converted to/from char, so you can try to cast with (char*)msg and see if that solves the problem.
What does C print format specifier ' %ldHz ' mean?
The format specifier is %ld for type long, Hz is part of the output string (Hertz).

printing size_t: format '%lu' expects argument of type 'long unsigned int'

I try to print size_t by casting to unsigned long (as suggested in the book "C programming a modern approach) like the following:
printf("size:%lu, bsize:%lu", (unsigned long)size, (unsigned long)bsize);
printf("size:%lu, bsize:%lu", ((unsigned long)size), ((unsigned long)bsize));
The first line would give me warning (gcc):
warning: format '%lu' expects argument of type 'long unsigned int', but argument 2 has type 'size_t' [-Wformat]
What's the difference between the first line and the second line? All I did was putting extra parenthesis, what exactly does that do?
I know I can use "%z" but this problem bugs me.
Assuming there are no ugly #defines around
printf("size:%lu, bsize:%lu", (unsigned long)size, (unsigned long)bsize);
and
printf("size:%lu, bsize:%lu", ((unsigned long)size), ((unsigned long)bsize));
are equivalent.
And therefore they shall result in the same code/warnings/errors.
If they don't, there is something broken.

Conversion: uid_t to string, off_t to string

I am currently writing a systems programming homework and in one part i need to get some information of a file in a directory.
for the stat of file, we have ctime() function which converts time_t type to string and returns a pointer to it.
but how about the uid_t and off_t types? I searched through to internet and couldnt find any function.. Or if there does not exist any function, can you tell me how to implement such a function please?
Both are defined as arithmetic types (http://www.opengroup.org/onlinepubs/009695399/basedefs/sys/types.h.html), and in practice are positive and integral. So you can just cast to unsigned long long, and use sprintf with "%llu" to convert to string.
size_t and off_t are just unsigned integral types. (Edit: off_t is a long? See, the lesson is, check your headers!)
So use sprintf (or whatever) to convert them using the "%i" format specifier.
On edit: crap, you changed size_t to uid_t while I was answering. uid_t is defined in types.h; look there. (It's also an unsigned integral type, but an unsigned short.)
Linux' snprintf() supports the 'z' format specifier for values of type size_t. Not sure how portable this is, you'll need to inspect the "CONFORMS TO" section closely.
For off_t, you might need to cast to the largest unsigned integer type, i.e. unsigned long and use a "lu" specifier.
off_t is a long int: format = "%ld"
size_t is an unsigned int: format = "%u"
You can use these format in sprintf function to convert into a char*.
Using gcc 5.4.0 the following line:
printf("user #%ld did this or that!\n", uid);
raised the following warning:
..main.c:133:9: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 2 has type ‘uid_t {aka unsigned int}’ [-Wformat=]
I would suggest you to do the same and check your compiler's output. =)

Resources