portable way to deal with 64/32 bit time_t - c

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.

Related

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.

C sprintf breaks with byte parameters (Keil compiler)

I have code running in 2 projects/platforms. It works in one, not the other. Code is like this:
uint8_t val = 1;
uint8_t buff[16];
sprintf(buff, "%u", val);
The expected output is "1" (gcc) but on one compiler (Keil) it returns "511", which in hex is 0x1FF. Looks like its not promoting the byte to int with this compiler. This is confirmed because it works ok if I do this:
sprintf(buff, "%u", (int)val);
My question is this: why does one compiler do what I consider the 'right thing', and one does not? Is it my incorrect expectations/assumptions, a compiler setting, or something else?
For maximum portability, you can use these macros from inttypes.h: (there are others)
PRId8, PRIx8, PRIu8
PRId16, PRIx16, PRIu16
PRId32, PRIx32, PRIu32
Normally (as I expected):
#define PRIu8 "u"
But for the Keil compiler in this case:
#define PRIu8 "bu"
e.g.,
printf("0x%"PRIx8" %"PRIu16"\n", byteValue, wordValue);
That's pretty cumbersome though. I suggest more friendly compilers.
It's amazing what you don't know about this stuff even after doing it for decades.
Your assumption may be correct, or incorrect. It depends on the compiler implementation. All modern (or should say smart) compiler will do that like you mentioned. But Keil, as of ver. 9.02, you need to specify correct variable length for printf.
This is Keil C's way handling all kinds of printf functions.
You need to specify exactly how long it is. All regular are for 16-bit (unsigned) integer including %d, %x, and %u. Use modifier 'b' for 8-bit and 'l' for 32-bit. If you gave wrong length, you would get wrong number. Even worse, the rest of the variables are all wrong.
For example, to use 8-bit 'char', you use '%bd' (%bu, and %bx), and %ld, %lu, and %lx for 32-bit 'long'.
char c = 0xab;
printf("My char number is correctly displayed as '0x%02bx'\n", c);
Also note, likewise, to get numeric data from sscanf, it's same. The following example is to get a 32-bit long variable using sscanf:
long var;
char *mynum = "12345678";
sscanf(mynum, "%ld", &var);
Variable var contains number 12345678 after sscanf.
Hereunder shows the length for variables used in printf family for Keil.
%bd, %bx, %bu - should be used for 8-bit variables
%d, %x, %u - should be used for 16-bit variables, and
%ld, %lx, %lu - should be used for 32-bit variables

Format specifiers for implementation-defined types like time_t

I want to make my code more platform-/implementation-independent. I don't know what a time_t will be implemented as on the platform when the code is being compiled. How do I know the type of t to determine what format specifier to use?
...
time_t t = time(NULL);
printf("%s", t);
...
Usually you can use a cast to convert the operand to some type for which you know the right format.
Your proposed solution:
time_t t = time(NULL);
printf("%s", t);
clearly will not work, since time_t is a numeric type, not char*.
We know, in general, that time_t is an arithmetic type. Something like this:
printf("%ld\n", (long)t);
is likely to work on most systems. It can fail (a) if time_t is an unsigned type no wider than unsigned long and the current value of t exceeds LONG_MAX, or (b) if time_t is a floating-point type.
If you have C99 support, you can use long long, which is a little better:
printf("%lld\n", (long long)t);
If you really want to go overboard with portability, you can detect what kind of type time_t is:
if ((time_t)-1 > 0) {
// time_t is an unsigned type
printf("%ju\n", (uintmax_t)t);
}
else if ((time_t)1 / 2 > 0) {
// time_t is a signed integer type
printf("%jd\n", (intmax_t)t);
}
else {
// time_t is a floating-point type (I've never seen this)
printf("%Lg\n", (long double)t);
}
You might want to tweak the %Lg format to something like %Lf or %.10Lf, depending on what output format you want.
Again, this assumes C99 support -- and you'll need #include <stdint.h> to make uintmax_t and intmax_t visible.
time_t and clock_t are a bit unusual, in that the standard says only that they're arithmetic type capable of representing times. (In principle they could be complex types, but I'd say ignoring that possibility is worth the risk.)
In most other cases, you'll probably know whether a given type is signed, unsigned, or floating-point, and you can just convert to the widest type of that kind.
Note that if you don't know how time_t is represented, you probably won't understand the output of the printf (such as 1379375215) either -- unless your goal is to figure that out.
(If you were programming in C++ rather than C, std::cout << t << "\n"; would automatically use the correct overloaded operator<<.)
If you want human-readable output (like Mon 2013-09-16 16:46:55 PDT), you'll want to use one of the conversion functions declared in <time.h>, such as asctime() or strftime().
Generally, the way to display the value of a time_t is to break down its components to a struct tm using gmtime or localtime and display those or convert them as desired with strftime, or ctime to go directly from time_t to a string showing local time.
If you want to see the raw value for some purpose, the C standard specifies that time_t is real, which means it is integer or floating-point (C 2011 (N1570) 6.2.5 17). Therefore, you should be able to convert it to double and print that. There is some possibility that time_t can represent values that double cannot, so you might have to guard against that if you want to take care regarding exotic implementations. Since difftime returns the difference of two time_t objects as a double, it seems C does not truly support time_t with more precision than a double.
You can use difftime() to obtain a double:
time_t t = time(NULL);
printf("seconds 1970->now: %.f\n", difftime(t, (time_t) 0));
It is simple and I think it is portable.
The C standard says time_t will be a 'real type' (meaning an integer type or a floating point type, though in practice it is always an integer type).
With time_t, your best bet is to format it with strftime() after analyzing it with localtime() or gmtime() — this can be done portably.
Unportably, you have to determine by some mechanism what is the correct format specifier. You might use PRI_[Xxodi]_time and SCN_[Xxodi]_time or something similar as a non-standard but close-to-standard (without trampling on the reserved namespace — which is names starting PRI or SCN followed by a lower-case letter or X). You use some mechanism to specify that...encapsulating the unportable information in one place.

printf with sizeof on 32 vs 64 platforms: how do I handle format code in platform independent manner?

I have some code that prints the amount of memory used by the program. The line is similar to this:
printf("The about of RAM used is %u", anIntVariable*sizeof(double) );
where anIntVariable is an int variable for the number of elements of the double array. Anyhow, on 32-bit systems I never had any problems but on 64-bit systems, I get a compiler warning about using "%u" for a unsigned long integer. Using "%lu" as the format code fixes the problem on 64-bit but causes the compiler to complain on 32-bit because the type is back to unsigned int. I've found that, indeed, sizeof(double) returns a different value on 32 vs 64 bit systems. I've found some webpage guides to convert code from 32 bit to 64 bit But I'd rather have code that works on both instead of just converting back and forth.
How do I write this line in a platform independent way? I know many ways I could do it using preprocessor directives but that seems like a hack. Surely there's an elegant way that I'm not realizing.
Portable printf identifiers are provided in the include file inttypes.h or here.
This include file has many portable identifiers for your specific runtime. For your example, you want PRIuPTR, which means "PRintf Identifier unsigned with size of up to a pointer's size".
Your example will then be:
printf("The amount of RAM used is %" PRIuPTR, anIntVariable*sizeof(double) );
Results on 64bit Linux with GCC 4.3 (int anIntVariable = 1):
$ gcc test.c -m32 -o test && ./test
The amount of RAM used is 8
$ gcc test.c -o test && ./test
The amount of RAM used is 8
For completeness sake, there are identifiers for scanf too, whose prefixes are SCN.
The return value of sizeof is a size_t. If you're using a C99 compliant compiler it looks like you can use %zd%zu for this.
D'oh: %zu (unsigned) of course. Thanks, ony.
First of all, you should match the "%" specifier with the actual data type you want to print. sizeof returns the data type size_t, and just as you shouldn't try to print a float using a "%d" specifier, you shouldn't try to print a size_t with "%u" or "%d" or anything that doesn't really mean size_t.
The other replies have given some good ways to handle this with newer compilers ("%z" and PRIu32), but the way we used to do this was simply to cast the size_t to unsigned long, and then print it using "%lu":
printf("The amount of RAM used is %lu", (unsigned long)(anIntVariable*sizeof(double)) );
This will not work on systems where size_t is wider than a long, but I don't know of any such systems, and I'm not even sure if the standard allows it.

Integer Overflow

I have an unsigned long long that I use to track volume. The volume is incremented by another unsigned long long. Every 5 seconds I print this value out and when the value reaches the 32 bit unsigned maximum the printf gives me a negative value. The code snippet follows:
unsigned long long vol, vold;
char voltemp[10];
vold = 0;
Later...
while (TRUE) {
vol = atoi(voltemp);
vold += vol;
fprintf(fd2, "volume = %llu);
}
What am I doing wrong? This runs under RedHat 4 2.6.9-78.0.5.ELsmp gcc version 3.4.5
Since you say it prints a negative value, there must be something else wrong, apart from your use of atoi instead of strtoull. A %llu format specifier just doesn't print a negative value.
It strongly looks like the problem is the fprintf call. Check that you included stdio.h and that the argument list is indeed what is in the source code.
Well I can't really tell because your code has syntax errors, but here is a guess:
vol = atoi(voltemp);
atoi converts ascii to integer. You might want to try atol but that only gets it to a long, not a long long.
Your C standard library MIGHT have atoll.
You can't use atoi if the number can exceed the bounds of signed int.
EDIT: atoll (which is apparently standard), as suggested, is another good option. Just keep in mind that limits you to signed long long. Actually, the simplest option is strtoull, which is also standard.
Are you sure fprintf can take in a longlong as a parameter, rather than a pointer to it? It looks like it is converting your longlong to an int before passing it in.
I'd guess the problem is that printf is not handling %llu the way you think it is.
It's probably taking only 32 bits off the stack, not 64.
%llu is only standard since C99. maybe your compiler likes %LU better?
For clarification the fprintf statement was copied incorrectly (my mistake, sorry). The fprintf statement should actually read:
fprintf(fd2, "volume = %llu\n", vold);
Also, while admittedly sloppy the maximum length of the the array voltemp is 9 bytes (digits) which is well within the limits of a 32-bit integer.
When I pull this code out of the program it is part of and run it in a test program I get the result I would expect which is puzzling.
If voltemp is ever really big, you'll need to use strtoull, not atoi.

Resources