Conversion: uid_t to string, off_t to string - c

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

Related

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

C: printf with typedef unsigned long long int

I have a typedef that contains a 64bit memory address in hex, but using %x with printf is giving me the following error:
error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'address'\
This is my declaration of the typedef:
typedef unsigned long long int address;
How can I print this hexadecimal value? Thanks!
Edit: the memory address is not a pointer, I am reading hexadecimal values from another file and need to store these 64bit addresses numerically using the typedef I defined.
The %x conversion specifier expects its corresponding argument to have type unsigned int. To print an unsigned long long, you need to use the length modifier ll, or %llx.
Handy Table
Length Modifier Type
––––––––––––––– ––––
ll (unsigned) long long
l (unsigned) long
h (unsigned) short
hh (unsigned) char
j intmax_t, uintmax_t
z size_t
t ptrdiff_t
L long double
So to format an unsigned char value as hex, you’d use %hhx or %hhX. To format a short as decimal, you’d use %hd. To format an unsigned long as octal, you’d use %lo.
Gratuitous rant
You’ve just illustrated why I tend to avoid using the typedef facility for primitive types.
You’ve created what’s known as a leaky abstraction - you’ve ostensibly created a new type to represent an address value, but you have to know that it has been implemented as an unsigned long long in order to use it correctly.
If you’re going to create an abstract type, you need to go all the way and create abstractions (functions, macros, etc.) for all operations on that type, including input and output (and assignment, and validation, etc.).
It’s generally not worth the effort for primitive types, so I’d avoid using typedef for this purpose.

Are there better ways to ease this warning?

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.

warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘__dev_t’ [-Wformat=]

I am using stat file system in a program and I want to print the device id using
printf("\nst_dev = %s\n",buf.st_dev);
but i am getting error:
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2
has type ‘__dev_t’ [-Wformat=]
What should b used instead of %s here?
st_dev is of type dev_t which is an integer type as per POSIX definition:
dev_t shall be an integer type.
So printing it using %s certainly wrong. There's no portable way to print it because there's no format specifier defined for it in POSIX. You could use intmax_t to print it:
printf("\nst_dev = %jd\n", (intmax_t)buf.st_dev);
If intmax_t isn't available (such as C89 systems) then you could cast it to long.
There isn't a specific format specifier for dev_t (aka __dev_t), but it should be an integer type of some kind. If you follow the rabbit hole of typedefs and defines in the standard header files you should eventually reach the base definition for the type. On my system the chain is:
typedef __dev_t dev_t;
__STD_TYPE __DEV_T_TYPE __dev_t;
#define __DEV_T_TYPE __UQUAD_TYPE
#define __UQUAD_TYPE __u_quad_t
__extension__ typedef unsigned long long int __u_quad_t;
Therefore %lu would work. It may be different on your system, but chances are it is something similar to unsigned long int. It's usually helpful to investigate these things when you encounter them, you never know what you might learn about how things work under the hood.

What is the need of hh and h format specifiers?

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.

Resources