The Open Group Base Specifications Issue 7, 2018 edition says that "time_t shall be an integer type". Issue 8, 2024 edition says "time_t shall be an integer type with a width of at least 64 bits".
C merely says that time_t is a "real type capable of representing times". A "real type", as C defines the term, can be either integer or floating-point. It doesn't specify how time_t represents times; for example, a conforming implementation could represent 2024-12-27 02:17:31 UTC as 0x20241227021731.
It's been suggested that time_t should be unsigned so a 32-bit integer can represent times after 2038 (at the cost of not being able to represent times before 1970). Fortunately this did not catch on, and with the current POSIX requiring 64 bits, it wouldn't make much sense.
But the relevant standards don't forbid an unsigned time_t.
> 1. Don’t assume a terminal type. Look at `TERM` and use termcap/terminfo or a library built atop them for anything beyond line-oriented plain text output, or least assume a plain teletype unless you specifically recognize the user’s terminal type.
I agree, but these days I think you can mostly get away with assuming VT100-compatible behavior.
> 4. Use the standard `<sysexits.h>` exit codes. They exist for a reason and they make use of your tool within pipelines and programs more straightforward because they make it easier to trace why a failure occurred.
I just took a look at this header file. (It defines exit codes starting at 64.) I'm not sure I've ever seen a program that uses these codes. Many programs for UNIX-like systems just use exit(1) for generic errors, or maybe something to distinguish between data errors and usage errors such as unrecognized command-line options. I mostly use Linux; maybe it's more common on BSD-based systems (it appeared "somewhere after 4.3BSD"). For example curl defines nearly 100 distinct error codes; none of them are based on <sysexit.h>.
> 5. Include both in-binary `--help`/usage information and a man page. Often a user will just need a quick refresher on argument syntax, which is what the built-in help text is for; the man page should be a comprehensive reference with examples. It should never defer to a web page or GNU `info`—it’s fine if those exist too and are pointed out, but they should not be the primary user reference.
In practice, GNU `info` tends to be the primary reference for GNU programs. The man page is often missing a lot of information.
In most practices I've seen, >=128 is reserved because it uses a special bit flag, and fixed codes 64-127 are reserved. But <64 is treated as a free-for-all. Most people are only using existing code 1, but if you have a reason to return specific different exit codes you need more than a couple.
You can (usually) run your remote tmux in a window in your local tmux session; then you can use the local tmux session's copy/paste features.
I commonly run "ssh remote-system" in a tmux window, then attach to a tmux session on the remote system.
If you nest tmux sessions like this, you have to type the prefix character (Ctrl-B by default; I use Ctrl-Space) twice for the nested session to see it.
> Don't use color as the only indication of something. The user's terminal might not display it, and it probably won't be preserved in copy&paste into notes.
And some of us choose to disable color by default. (Yes, I'm old-fashioned.)
Code that assumes time_t is the same width as int is already broken, and won't work on typical 64-bit systems were int is 32 bits and time_t is 64 bits.
In any case, I'm not sure I've ever seen any such code.
> Code that assumes time_t is the same width as int is already broken
You missed the point. It is meant that code _built for the same target_ "assumes" all instances use the same definition of time_t, and not about such a guarantee across different targets.
That's why Gentoo solution to redefine target is cumbersome but finally working - as radical as axe.
The only reason that might be surprising is that the "return *p;" statement refers to the value of an object at a point (textually) before its definition. But the lifetime of the object named "v" begins on entry to the innermost compound statement enclosing its definition -- in this case the body of "main".
Space for "v" is allocated on entry to "main". It's initialized to 1 when its definition is reached. The "return *p;" statement appears before the definition of "v" in the program source, but is executed after its definition was reached at run time, and within its lifetime.
It's important to remember that scope and lifetime are two different things.
The scope of an identifier is the region of program text in which the identifier is visible; for "v" it extends from the definition to the closing "}". The lifetime of an object is the time span during execution in which it exists; for "v" it extends from the time when execution reaches the opening "{" to the time when execution reaches the closing "}". Formally, storage for "n" is allocated at the beginning of its lifetime and deallocated at the end of its lifetime. Compilers can and do optimize allocation and deallocation, as long as the visible behavior is consistent.
Aside: If "v" were a VLA (variable length array, introduced in C99, made optional in C11) its lifetime would begin when execution reaches its definition.
Can't it reuse v's memory for other things before v is defined? Say there is "int a = 4;" at the beginning of main that is no longer used when it reaches "int v = 1;", can't a & v share same memory location?
A compiler can reuse memory as much as it likes -- but only if the visible behavior of the program is consistent with the language requirements.
If you write:
{
int n = 42;
printf("%d\n", n);
}
in the abstract machine, `sizeof (int)` bytes are allocated on entry to the block and deallocated on exit, but a compiler can legally replace the entire block with `puts("42")` and not allocate any memory for `n`.
Memory for objects defined in nested blocks is logically allocated on entry to the block, but compilers commonly merge the allocation into the function entry code. Even so, objects in parallel blocks can certainly share memory:
int main(void) {
{
int a;
}
{
int b; // might share memory with a
}
}
Logically, memory for `a` is allocated on entry to the first inner block, and memory for `b` on entry to the second inner block. Compilers will typically allocate all the memory on entry to `main`, but can use the same address for `a` and `b`.
As written, without introducing VLAs or additional blocks, no. C23 §6.2.4(5–6):
> An object whose identifier is declared with no linkage and without the storage-class specifier `static` has automatic storage duration [...].
> For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial representation of the object is indeterminate. If an initialization is specified for the object and it is not specified with `constexpr`, it is performed each time the declaration or compound literal is reached in the execution of the block [...]; otherwise, the representation of the object becomes indeterminate each time the declaration is reached.
That is, a local variable is live from the moment the block that contains its declaration is entered (however and wherever that happens) until it is left (ditto), but is initialized or, for lack of a better word, uninitialized each time execution passes that declaration (however many times that happens, including none). This is despite the fact that at compile time the variable’s name is not in scope until the = introducing its initializer (or the place where such a = would go if there isn’t one). Modulo its smaller feature set, C89 §6.1.2.4(3) stipulates the same.
In addition to GGP’s deliberately confusing example, this permits the much more reasonable and C89-compatible
switch (x) {
int i, j;
case 1:
/* use i and j */
break;
case 2:
/* use i and j */
break;
}
The only exception is locals of variably modified type (e.g. variable-length arrays), whose declarations you can’t jump over on pain of undefined behaviour.
No wonder basically every C compiler allocates a single stack frame at function entry.
I think another problem exposed by %n was that you can't easily compose format strings. Sure, `printf(str)` where `str` is a user input would be easy to detect and can be automatically turned into `printf("%s", str)` with some macro hack, but `printf(fmt, ...)` where `fmt` is composed from multiple partial format strings would be harder to reason.
The Open Group Base Specifications Issue 7, 2018 edition says that "time_t shall be an integer type". Issue 8, 2024 edition says "time_t shall be an integer type with a width of at least 64 bits".
C merely says that time_t is a "real type capable of representing times". A "real type", as C defines the term, can be either integer or floating-point. It doesn't specify how time_t represents times; for example, a conforming implementation could represent 2024-12-27 02:17:31 UTC as 0x20241227021731.
It's been suggested that time_t should be unsigned so a 32-bit integer can represent times after 2038 (at the cost of not being able to represent times before 1970). Fortunately this did not catch on, and with the current POSIX requiring 64 bits, it wouldn't make much sense.
But the relevant standards don't forbid an unsigned time_t.