Format specifiers for fixed-width integers? - c

What are the format specifiers to use for printf when dealing with types such as int32_t, uint16_t and int8_t, etc.?
Using %d, %i, etc. will not result in a portable program. Is using the PRIxx macros the best approach?

Is using the PRIxx macros the best approach?
As far as I know, yes.
Edit: another solution is to cast to a type that is at least as wide as the one you want to print. For example int is at least 2 bytes wide, to can print a int16_t with printf("%d\n", (int)some_var).

Yes, if you're using the new types, you really should be using the new format specifiers.
That's the best way to do it since the implementation has already done the grunt work of ensuring the format strings will be correct for the types.
So, for example:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main (void) {
int32_t i32 = 40000;
printf ("%d\n", i32); // might work.
printf ("%" PRId32 "\n", i32); // will work.
return 0;
}
shows both ways of doing it.
However, there's actually no guarantee that the first one will do as you expect. On a system with 16-bit int types for example, you may well get a different value.

Related

Is it valid to use %lu in a format string for printf where PRIu32 would be expected?

Assuming I have, for example, a variable i of type uint32_t. The expected way to print it would be like that:
printf("%"PRIu32"\n", i);
However, it should be noted that it is required for long unsigned int to be at least 32bits wide. The correct specifier for long unsigned int is %lu. Thus, can the above statement be replaced with:
printf("%lu\n", i);
I’d suppose yes, since I can see no reason why not. However, if yes, then this would remove the need for existence of these macroified specifiers like PRIu32, so I figure I’d better ask.
The reason I’m asking it is that I’d like to create a format string for printf dynamically, and it’d be hard to allocate space for this format string if I don't know the size of the string PRIu32 expands to (and whether sizeof(PRIu32) is valid or not may be worthy of a separate question).
In any case, I suppose it should be valid to write:
printf("%lu\n", (long unsigned)i);
Thus, can the above statement be replaced with:
printf("%lu\n", i);
I’d suppose yes, since I can see no reason why not.
No, because long unsigned int can be larger than 32 bits, or if exactly 32-bits can nevertheless have a different representation than does uint32_t.
In any case, I suppose it should be valid to write:
printf("%lu\n", (long unsigned)i);
Yes, and as you observed, it is also safe, because long unsigned int is required to be able to represent all the values that a uint32_t can take.

printf and (signed) int of unknown size

I have a platform dependent type defined in my code:
typedef uint64_t myType;
However on some more limited platform, it might be 32 bits.
How do I printf it?
As in, in the current situation, I can use %llu, but if on another platform it's 32 bits, this is not the best idea.
I thought about using some macros, but would anyone know of a better way? I'd love to hear about some format specifier that could take the length from the next argument, for example.
Since you have platform-specific types, it should be easy enough to use platform-specific format strings as well, something like:
#ifdef USING_64_bits
typedef uint64_t myType;
#define MY_TYPE_FMT PRIu64
#else
typedef uint32_t myType;
#define MY_TYPE_FMT PRIu32
#endif
Then you can use it with:
myType var1 = 42, var2 = 99;
printf ("%6" MY_TYPE_FMT ", %019" MY_TYPE_FMT "\n", var1, var2);
The extraction of the % from the format string allows you to insert other format specifiers dynamically, such as field widths and padding characters.
You'll also notice that I've avoided the %llu-style format specifiers, you should be using the more targeted ones in inttypes.h since the implementation will give you the correct one for your type.
Just cast it up to the largest-possible integer type matching the desired signedness and use the format for that, either:
printf("%jd", (intmax_t)x);
or:
printf("%ju", (uintmax_t)x);
(The question title asks for signed but the body is using unsigned examples, so I've covered both.)
This is a lot less ugly/more readable than using the PRI* macros suggested in the other answer, and also works for types where you don't inherently know the right PRI macro to use, like off_t.

reading hex data from file fscanf format compile time warning

I'm reading some data from a file. The format is stated tobe
ASCII text with UNIX-style
line-endings, a series of 32-bit
signed integers in hexadecimal.
e.g
08000000
I'm using fscanf to read in this data.
long data_size;
FILE *fp;
fp=fopen("test01.bin", "r"); // open for reading
if (fp==0) {cerr << "Error openeing file"<<endl; return 1;}
fscanf(fp, "%x", &data_size);
Everything runs ok with my test file but I get the compile-time warning,
warning: format ‘%x’ expects type ‘unsigned int*’, but argument 3 has type ‘long int*’
however a hex value is unsigned and is being cast to a long dose this matter? As long will take the most significant bit as notifying the sign? Or will I end up with problems? Or am I well off the mark in my understanding?
Thanks
You should support the same pointer type as the warning states, or else you will run into serious troubles if you want to port your code to other architectures (e.g: 64 bit architectures) where long has a different size than int. This is especially tricky if you are using pointers. (I once had a bug originating from exactly this problem)
Just use int data_size and you will be fine.
The problem is that %x requires an unsigned int * to read the value in, but you have a long *. <stdint.h> header provides value types with fixed length, and <inttypes.h> defines corresponding macros for use with printf, scanf, and their derivatives. I think it'll be better for you to fscanf the data into an int32_t variable using the macro provided by <inttypes.h>:
#include <inttypes.h>
...
int32_t data_size;
fscanf(fp, "%" SCNx32, &data_size);

portable way to deal with 64/32 bit time_t

I have some code which is built both on Windows and Linux. Linux at this point is always 32bit but Windows is 32 and 64bit. Windows wants to have time_t be 64 bit and Linux still has it as 32 bit. I'm fine with that, except in some places time_t values are converted to strings. So when time_T is 32 bit it should be done with %d and when it is 64bit with %lld... what is the smart way to do this? Also: any ideas how I may find all places where time_t's are passed to printf-style functions to address this issue?
edit:
I came up with declaring TT_FMT as "%d" or "%lld" and then changing my printfs as in
printf("time: %d, register: blah") to be printf("time: " TT_FMT ", register: blah")
Is there a better way? And how do I find them all?
According to the C standard, time_t is an arithmetic type, "capable of representing times". So, it could be double for example. (Posix mentions this more explicitly, and also guarantees that time() returns the number of seconds since the Epoch—the latter is not guaranteed by the C standard.)
Maybe the cleanest solution is to convert the value to whatever type you want. You may want one of unsigned long long or unsigned long:
printf("%llu\n", (unsigned long long)t);
I think the only truly portable way is to use strftime to convert the time_t to a string.
If you're sure that you're only operating on platforms where time_t is an int, you could cast to intmax_t (from stdint.h) and print it using PRIdMAX (from inttypes.h).
If you want to go with the macro specifier, I would recommend one minor tweak. Instead of encapsulating the entire specifier, encapsulate just the modifier:
#ifdef 64_BIT_TIME
#define TT_MOD "ll"
#else
#define TT_MOD ""
#endif
and then using it like this:
printf("current time in seconds is: %" TT_MOD "u", time(0));
The reason why is that while you primarily want the second in decimal, every so often you may want hex (or perhaps you want leading 0's). By only having the modifier there, you can easily write:
"%" TT_MOD "x" // in hex
"%08" TT_MOD "d" // left pad with 0's so the number is at least 8 digits
Slight adjustment on Alok's answer, it's signed on both Windows and Linux, so:
printf("%lld\n", t);
is cleaner.

How to portably convert a string into an uncommon integer type?

Some background: If I wanted to use for, for instance, scanf() to convert a string into a standard integer type, like uint16_t, I’d use SCNu16 from <inttypes.h>, like this:
#include <stdio.h>
#include <inttypes.h>
uint16_t x;
char *xs = "17";
sscanf(xs, "%" SCNu16, &x);
But a more uncommon integer type like pid_t does not have any such thing; only the normal integer types are supported by <inttypes.h>. To convert the other way, to portably printf() a pid_t, I can cast it to intmax_t and use PRIdMAX, like this:
#include <stdio.h>
#include <inttypes.h>
#include <sys/types.h>
pid_t x = 17;
printf("%" PRIdMAX, (intmax_t)x);
However, there does not seem to be a way to portably scanf() into a pid_t. So this is my question: How to do this portably?
#include <stdio.h>
#include <sys/types.h>
pid_t x;
char *xs = 17;
sscanf(xs, "%u", &x); /* Not portable! pid_t might not be int! /*
I thought of scanf()ing to an intmax_t and then checking that the value is within pid_t’s limits before casting to pid_t, but there does not seem to be a way to get the maximum or minimum values for pid_t.
There is one robust and portable solution, which is to use strtoimax() and check for overflows.
That is, I parse an intmax_t, check for an error from strtoimax(), and then also see if it "fits" in a pid_t by casting it and comparing it to the original intmax_t value.
#include <inttypes.h>
#include <stdio.h>
#include <iso646.h>
#include <sys/types.h>
char *xs = "17"; /* The string to convert */
intmax_t xmax;
char *tmp;
pid_t x; /* Target variable */
errno = 0;
xmax = strtoimax(xs, &tmp, 10);
if(errno != 0 or tmp == xs or *tmp != '\0'
or xmax != (pid_t)xmax){
fprintf(stderr, "Bad PID!\n");
} else {
x = (pid_t)xmax;
...
}
It is not possible to use scanf(), because, (as I said in a comment) scanf() will not detect overflows. But I was wrong in saying that none of the strtoll()-related functions takes an intmax_t; strtoimax() does!
It also will not work to use anything else than strtoimax() unless you know the size of your integer type (pid_t, in this case).
It depends on exactly how portable you want to be. POSIX says that pid_t is a signed integer type used to store process IDs and process group IDs. In practice, you could assume with safety that long is big enough. Failing that, your intmax_t must be big enough (so it will accept any valid pid_t); the trouble is, that type could accept values that are not legitimate in pid_t. You're stuck between a rock and a hard place.
I would use long and not worry very much about it except for an obscure comment somewhere that a software archaeologist of 100 years hence will find and observe gives a reason why the 256-bit CPU is creaking to a halt when handed a 512-bit value as a pid_t.
POSIX 1003.1-2008 is now available on the web (all 3872 pages of it, in PDF and HTML). You have to register (free). I got to it from the Open Group Bookstore.
All that I see there is that it must be a signed integer type. Clearly, all valid signed integer values fit into intmax_t. I cannot find any information in <inttypes.h> or <unistd.h> that indicates PID_T_MAX or PID_T_MIN or other such values (but I've only just this evening got access to it, so it could be hidden where I haven't looked for it). OTOH, I stand by my original comment - I believe that 32-bit values are pragmatically adequate, and I would use long anyway, which would be 64-bit on 8-bit machines. I suppose that roughly the worst thing that could happen is that an 'appropriately privileged' process read a value that was too large, and sent a signal to the wrong process because of a mismatch of types. I'm not convinced I'd be worried about that.
...oooh!...p400 under <sys/types.h>
The implementation shall support one or more programming environments in which the widths
of blksize_t, pid_t, size_t, ssize_t, and suseconds_t are no greater than the width of type long.
If you are really concerned you can _assert(sizeof(pid_t) <= long) or whatever type you choose for your '%' stuff.
As explained in this answer, the spec says signed int. If 'int' changes, your '%u' by definition changes with it.

Resources