It seems remarkably difficult to find out what a valid range of values is for time_t.
It is on some platforms 32 bit, on most 64 bit, and so can easily enough be set to LONG_MAX. However trying to then use that value doesn't really work properly. For instance you can't pass it to localtime and change it into a struct tm.
A quick test program to binary search the value tells me it is 67768036191676799. That corresponds to the end of year 2147483647, so that makes sense as a value. But is it this specified anywhere, and is there any reasonable, platform independent value for a maximum usable time_t?
Indeed, the specification for time_t and clock_t are implementation defined (C99 7.23.1).
This is one of those things where I would recommend not generating these values yourself, but rely on the implementation to create them for you such as with mktime(), and use the struct tm for directly manipulating the time. -1 is the only value of time_t that is a "good" value you can use on your own.
I would specifically suggest that you not treat it as a 32 bit value of any type, as jgm suggests. You just never know if some strange embedded compiler will want to use a 16 bit time or 18 or who knows.
The safest way to use it is as a 32-bit signed, as long as you're happy with it not working 25 years from now.
Otherwise you're going to have to test the type yourself on whichever platforms you run on and act accordingly.
tm_year has type int, so if you will be converting to struct tm, the largest meaningful time_t is the value corresponding to year INT_MAX.
Related
According to the documentation the struct tm *gmtime(const time_t *timer); is supposed to convert the time_t pointed to by timer to a broken down time.
Now is there a reason why they decided to make the function take a pointer to the time_t instead of passing the time_t directly?
As far as I can see time_t is of arithmetic type and should therefore have been possible to pass directly (also I find it reasonable that it would have fit into a long). Also there seem to be any specific handling of the NULL pointer (which could have motivated passing a pointer).
Is there something I'm missing? Something still relevant today?
From what I've seen, it's more a historical quirk. When time.h was first introduced, and with it functions like time, it used a value that could not be returned (ie: no long int etc). The standard defined an obscure time_t type that still leaves a lot of room for vendors to implement in weird ways (it has to be an arithmetic type, but no ranges or max values are defined for example) - C11 standard:
7.27.1 Components of time.
[...]
3 .The types declared are size_t (described in 7.19);
clock_t
and
time_t
which are real types capable of representing times;
4. The range and precision of times representable in clock_t and time_t are
implementation-defined.
size_t is described, in C11, as "is the unsigned integer type of the result of the sizeof operator;"
In light of this, your comment ("I find it reasonable that it would have fit into a long") is an understandable one, but it's incorrect, or at the very least inaccurate. POSIX, for example requires time_t to be an integer or real floating type. A long fits that description, but so would a long double which doesn't fit in a long. A more accurate assumption to make would be that the minimum size of time_tis a 32bit int (until 2038 at least), but that time_t preferably is a 64bit type.
Anyway, back in those days, if a value couldn't be returned, the only alternative was to pass the memory to the function (which is a sensible thing to do).
This is why we have functions like
time_t time(time_t *t);
It doesn't really make sense to set the same value twice: once by returning it, and once using indirection, but the argument is there because originally, the function was defined as
time(time_t *t)
Note the lack of a return type, if time were added today, it'd either be defined as void time( time_t * ), or if the committee hadn't been drinking, and realized the absurdity of passing a pointer here, they'd define it as time_t time ( void );
Looking at the C11 standard regarding the time function, it seems that the emphasis of the functions' behaviour is on the return value. The pointer argument is mentioned briefly but it certainly isn't given any significance:
7.27.2.4 The time function
1. Synopsis
#include <time.h>
time_t time(time_t *timer);
2. Description
The time function determines the current calendar time. The encoding of the value is
unspecified.
3. Returns
The time function returns the implementation’s best approximation to the current
calendar time. The value (time_t)(-1) is returned if the calendar time is not
available. If timer is not a null pointer, the return value is also assigned to the object it
points to.
The main thing we can take from this is that, as far as the standard goes, the pointer is just a secondary way of getting at the return value of the function. Given that the return value indicates something went wrong ((time_t)(-1)), I'd argue that we should treat this function as though it was meant to be time_t time( void ).
But because the old implementation is still kicking around, and we've all gotten used to it, it's one of those things that should've been marked for deprecation, but because it never really was, it probably will be part of the C language for ever...
The only other reason why functions use time_t like this (const) is either historical, or to maintain a consistent API across the time.h API. AFAIK that is
TL;DR
Most time.h function use pointers to the time_t type for historical, compatibility, and consistency reasons.
I knew I read that stuff about the early days of the time function before, here's a related SO answer
The reason is probably that in the old days a parameter could be no larger than an integer, so you couldn't pass a long and had to do that as a pointer. The definition of the function never changed.
When I reading some code, for integer, they use bunch of different type such as size_t, uint32, uint64 etc.
What is the motivation or purpose to do this?
Why not just use int?
Related to platform-cross? Or low-level relevant.
Sometimes, the code make sense to me because they just want 32 bit int or something.
But, what is size_t?
Please help me make this clear.
These are for platform-independence.
size_t is, by definition, the type returned by sizeof. It is large enough to represent the largest object on the target system.
Not so many years ago, 32 bits would have been enough for any platform. 64 bits is enough today. But who knows how many bits will be needed 5, 10, or 50 years from now?
By writing your code not to care -- i.e., always use size_t when you mean "size of an object" -- you can write code that will actually compile and run 5, 10, or 50 years from now. Or at least have a fighting chance.
Use the types to say what you mean. If for some reason you require a specific number of bits (probably only when dealing with an externally-defined format), use a size-specific type. If you want something that is "the natural word size of the machine" -- i.e., fast -- use int.
If you are dealing with a programmatic interface like sizeof or strlen, use the data type appropriate for that interface, like size_t.
And never try to assign one type to another unless it is large enough to hold the value by definition.
The motivation to use them is because you can't rely on int, short or long to have any particular size - a mistake made by too many programmers far too many times in the past. If you look not too far back in history, there was a transition from 16 bit to 32 bit processors, which broke lots of code because people had wrongly relied on int being 16 bits. The same mistake was made thereafter when people relied on int to be 32 bits, and still do so even to this day.
Not to mention the terms int, short and long have been truly nuked by language designers who all decide to make them mean something different. A Java programmer reading some C will naively expect long to mean 64 bits. These terms are truly meaningless - they don't specify anything about a type, and I facepalm every time I see a new language released that still uses the terms.
The standard int types were a necessity so you can use the type you want to use. They should've deprecated int, short and long decades ago.
For info on size_t, see the Stack Overflow question: What is size_t in C?
You're right for uint32 and uint64 that they're just being specific about the number of bits that they would like, and that the compiler should interpret them as unsigned.
There are many possible reasons for choosing an underlying type for an integer value. The most obvious one is the size of the maximum possible value that you can store -- uint32 will be able to store a number twice as large as int32, which might be desirable. int64 will be able to store a number much larger than int32 - up to 2^63 - 1 instead of 2^31 - 1.
There are other possible reasons as well. If you're directly reading binary data from some source (file, socket, etc), it is necessary to make sure it's interpreted correctly. If someone writes a uint32 and you interpret it as an int32, it's possible that you interpret a very large positive number as a negative number (overflow).
size_t is just a typedef for an unsigned int, usually 32-bit I believe.
For most day-to-day programming, the size of an integer doesn't really matter all that much. But sometimes it is good to be specific. This is especially useful in low-level or embedded programming. Another place it is useful is scientific or computationally intensive tasks where it might be wasteful to use an int that is bigger than necessary.
The advantage of size_t is that it is unsigned. On the one hand it nice to use size_t because it adds more information about what the argument should be (i.e not negitave). On the other hand it is less tying vs. unsigned int.
I noticed some programmers use unsigned long for tv_sec and tv_usec [when they copy them or operate with them] of timeval while they are defined as simply long.
Though it does make me wonder why they were defined like that when time usually goes forward.
Using long int for those variables will work until year 2038, and after that the tv_sec will overflow on machines where long is 4bytes.
timeval shall be defined as:
The <sys/time.h> header shall define the timeval structure that includes at least the following members:
time_t tv_sec Seconds.
suseconds_t tv_usec Microseconds.
You should notice that time_t type is used instead of long, but which is also a 32bit representation on some systems while there are even 64bit representations on other systems. In order to avoid overflow, time_t will probably be changed to an unsigned 32bit integer or a 64bit one.
That is why some are using unsigned long, as it will stop the overflow until year 2100+. You should use the time_t type instead, and you won't need to think about how long your program is supposed to run for in the future.
When unix time was invented, negative times probably made sense. Like, AT&T needed adequate timestamps for things that happened in the 1960s.
As for microseconds, if you subtract two values you can go into negative numbers with signed value, and into 4+billions with unsigned. Comparing with 0 seems more intuitive.
tv_sec has type time_t. tv_usec has type long, and needs to be signed because you will (on average 50% of the time) get negative results in tv_usec when subtracting timeval values to compute an interval of time, and you have to detect this and convert it to a borrow from the tv_sec field. The standard (POSIX) could have instead made the type unsigned and required you to detect wrapping in advance, but it didn't, probably because that would be harder to use and contradict existing practice.
There is also no reason, range-wise, for tv_usec to be unsigned, since the maximum range it really needs to be able to represent is -999999 to 1999998 (or several times that if you want to accumulate several additions/subtractions before renormalizing).
Variables of type int are allegedly "one machine-type word in length"
but in embedded systems, C compilers for 8 bit micro use to have int of 16 bits!, (8 bits for unsigned char) then for more bits, int behave normally:
in 16 bit micros int is 16 bits too, and in 32 bit micros int is 32 bits, etc..
So, is there a standar way to test it, something as BITSIZEOF( int ) ?
like "sizeof" is for bytes but for bits.
this was my first idea
register c=1;
int bitwidth=0;
do
{
bitwidth++;
}while(c<<=1);
printf("Register bit width is : %d",bitwidth);
But it takes c as int, and it's common in 8 bit compilers to use int as 16 bit, so it gives me 16 as result, It seems there is no standar for use "int" as "register width", (or it's not respected)
Why I want to detect it? suppose I need many variables that need less than 256 values, so they can be 8, 16, 32 bits, but using the right size (same as memory and registers) will speed up things and save memory, and if this can't be decided in code, I have to re-write the function for every architecture
EDIT
After read the answers I found this good article
http://embeddedgurus.com/stack-overflow/category/efficient-cc/page/4/
I will quote the conclusion (added bold)
Thus
the bottom line is this. If you want
to start writing efficient, portable
embedded code, the first step you
should take is start using the C99
data types ‘least’ and ‘fast’. If your
compiler isn’t C99 compliant then
complain until it is – or change
vendors. If you make this change I
think you’ll be pleasantly surprised
at the improvements in code size and
speed that you’ll achieve.
I have to re-write the function for every architecture
No you don't. Use C99's stdint.h, which has types like uint_fast8_t, which will be a type capable of holding 256 values, and quickly.
Then, no matter the platform, the types will change accordingly and you don't change anything in your code. If your platform has no set of these defined, you can add your own.
Far better than rewriting every function.
To answer your deeper question more directly, if you have a need for very specific storage sizes that are portable across platforms, you should use something like types.h stdint.h which defines storage types specified with number of bits.
For example, uint32_t is always unsigned 32 bits and int8_t is always signed 8 bits.
#include <limits.h>
const int bitwidth = sizeof(int) * CHAR_BIT;
The ISA you're compiling for is already known to the compiler when it runs over your code, so your best bet is to detect it at compile time. Depending on your environment, you could use everything from autoconf/automake style stuff to lower level #ifdef's to tune your code to the specific architecture it'll run on.
I don't exactly understand what you mean by "there is no standar for use "int" as "register width". In the original C language specification (C89/90) the type int is implied in certain contexts when no explicit type is supplied. Your register c is equivalent to register int c and that is perfectly standard in C89/90. Note also that C language specification requires type int to support at least -32767...+32767 range, meaning that on any platform int will have at least 16 value-forming bits.
As for the bit width... sizeof(int) * CHAR_BIT will give you the number of bits in the object representation of type int.
Theoretically though, the value representation of type int is not guaranteed to use all bits of its object representation. If you need to determine the number of bits used for value representation, you can simply analyze the INT_MIN and INT_MAX values.
P.S. Looking at the title of your question, I suspect that what you really need is just the CHAR_BIT value.
Does an unsigned char or unsigned short suit your needs? Why not use that? If not, you should be using compile time flags to bring in the appropriate code.
I think that in this case you don't need to know how many bits has your architecture. Just use variables as small as possible if you want to optimize your code.
It's not the largest integer type anymore now that there's "long long".
It's not a fixed-width type: It's 32 bits on some platforms and 64 on others.
It's not necessarily the same size as a pointer (for example, on 64-bit Windows)
So, does "long" have any meaning anymore? Is there ever a reason to declare a long instead of a ptrdiff_t or int64_t?
Is there ever a reason to declare a long instead of a ptrdiff_t or int64_t?
There never was in those cases. If you want a pointer difference, or a specifically 64-bit value, you should be using ptrdiff_t or int64_t. You should never have been using long in the first place, except maybe behind a platform-dependent typedef.
You should use long when you need at least a 32-bit integer, because int is only guaranteed to be at least 16 bits. If you need a native type, but need at least 32-bits, use long. If the 16-bit limitation is acceptable on some old platforms (that probably don't matter and your code probably won't be compiled on ever) then it doesn't particularly matter.
There's a subtle difference between still useful and something you should habitually use. The long type still thrives, as Chris Lutz noted behind many system and platform specific types (though, usually as unsigned).
When you know the data you will be working with is always going to fit in that type (agreeably, better if you also know the signedness), there is no particular reason not to use it, especially if you have limited space in a structure to work with.
However, in most cases, for the sake of future maintainers, its much better to use intxx_t or uintxx_t wherever possible. For instance, you don't know that a UNIX epoch date will always fit inside an unsigned long on 32 bit platforms (hence, time_t), unless you have it on good authority that the world will end before it rolls over :)